Can I save my Findstr result in a variable? - batch-file

I have this CSV:
The;Quick;Brown;Fox
I know how to get the String:
findstr /v "*" brownfox.csv
But how can i safe this string in a variable without using a For loop.
I want to process the string here to get the token count %i%
set var1=(the string from brownfox.csv)
set var2=%var1%
set i=0
:loopprocess
for /F "tokens=1* delims=;" %%A in ( "%var1%" ) do (
set /A i+=1
set var1=%%B
goto loopprocess )
echo The string contains %i% tokens.
I just need the whole text in the CSV in a variable.

I'm not sure why you cannot use for, but if you absolutely cannot use it, you need a temporary file:
>%TEMP%\foo.tmp findstr /v "*" brownfox.csv
<%TEMP%\foo.tmp set /p "var1="
However, from what you describe you don't need the variable at all. And you use a for loop anyway. Why not simply do
for /f "tokens=1* delims=;" %%A in ('findstr /v "*" brownfox.csv') do (
set /a i+=1
set "var1=%%B"
)

<brownfox.csv set /p "line="
FOR %%G IN (%line%) DO set /a count+=1
echo The string contains %count% tokens.
first line to get the (first line of) the file, second to count the tokens, third to write it to screen.

Just one other option for you. This splits the string up into a pseudo array of variables and also gives you a count of the number of variables in the array.
#echo off
setlocal EnableDelayedExpansion
set i=1
set /p "x="<brownfox.csv
set "x!i!=%x:;=" & set /A i+=1 & set "x!i!=%"
set x
echo %i%
pause
This technique was discovered in this thread on DosTips.com.

Related

Semicolons at beginning of the line, delims and FOR cycle

I cannot figure out how to correctly identify an empty/undefined variable having two semicolon one after one other or the line that starts with it.
This is the cycle:
for /F "delims=; tokens=1-7" %%m IN (testlist.txt) DO echo FUNCGROUP=%%r a=%%m b=%%n c=%%o d=%%p e=%%q f=%%s
I also tried adding "eol=;" and "......eol=" without success.
This is content of the first line of the file testlist.txt:
;xxxxxx;Active;;FALSE;con ter - dong;HWID000001;Item;sites/coll-
The result I need is, for the first cycle:
a=
b=xxxxxx
c=Active
d=
e=FALSE
f=con ter - dong
g=HWID000001
Thanks for any help.
To achieve that, you need to put a "NULL value" for empty fields in the lines of the file.
As there is no direct string substitution utilities with batch, you have to make substitutions beforehand to create "empty" fields. I suggest you to use a "NULL" character for empty fields like unbreakable space (Alt+0160).
In your case, this gives :
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
for /F "eol= tokens=*" %%l in (file.txt) DO (
SET "LINE=%%l"
SET "LINE=###!LINE:;;=; ;!###"
SET "LINE=!LINE:###;= ;!"
SET "LINE=!LINE:;###=; !"
SET "LINE=!LINE:###=!"
for /F "delims=; tokens=1-7" %%m in ("!LINE!") DO (
SET "RES=FUNCGROUP=%%r a=%%m b=%%n c=%%o d=%%p e=%%q f=%%s"
echo !RES: =!
)
)
Note that the SET "LINE=###!LINE:;;=; ;!###", SET "LINE=!LINE:###;= ;!" and the SET "LINE=!LINE:;###=; !" sections use the unbreakable space (Alt+0160) and replace beginning ";" with "Alt+0160;", the ending ";" with ";Alt+0160" and any following ";;" with ";Alt+0160;". The for loop parses then correctly the line, and next you just have to remove the unbreakable space to get "empty" variables.
EDIT: As brightly suggested by #jeb in the comments, you can also use quotes to handle empty fields. Each for loop variables can be then directly and simply unquoted.
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
for /F "eol= tokens=*" %%l in (file.txt) DO (
SET "LINE=%%l"
SET "LINE=###^"!LINE:;=^";^"!^"###"
SET "LINE=!LINE:###^";=^"^";!"
SET "LINE=!LINE:;^"###=;^"^"!"
SET "LINE=!LINE:###=!"
for /F "delims=; tokens=1-7" %%m in ("!LINE!") DO (
echo FUNCGROUP=%%~r a=%%~m b=%%~n c=%%~o d=%%~p e=%%~q f=%%~s
)
)
Unfortunately, you have not provided sufficient information for what you intend to do with your empty field, so this is only a demonstration to provide you with output similar to that which you've indicated in your question:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
For /F "Delims==" %%G In ("(Set Field[) 2>NUL") Do Set "%%G="
Set "i=0"
For /F UseBackQ^ Delims^=^ EOL^= %%G In ("testlist.txt") Do Call :GetFields "%%G"
Pause
GoTo :EOF
:GetFields
Set "Record=%~1"
Set /A i += 1
SetLocal EnableDelayedExpansion
Set "Index=1"
Set "Field[!Index!]=%Record:;=" & Set /A Index +=1 & Set "Field[!Index!]=%"
Echo(&Echo Record !i!
For /L %%G In (1,1,!Index!) Do If Not Defined Field[%%G] (Echo Field[%%G]=) Else Set Field[%%G]
Exit /B

Double Expansion on Array Index Windows Batch

Inside the for loop I'm trying to access the element at index count in CLs (this line of code: echo !!CLs[!count!]!!) , but I'm not sure how to do this. I don't really understand how expansion works in this case, so what you see below it me trying something out of no where.
#ECHO off
setlocal enableextensions enabledelayedexpansion
SET CLs[0]=#
SET /A count = 0
FOR /F "tokens=5" %%I IN ('some command') DO (
echo !!CLs[!count!]!! :: THIS LINE
IF NOT %%I == CLs[!count!] (
SET /A count += 1
SET CLs[!count!]=%%I
)
)
echo The item is %CLs[10]%
endlocal
Thanks
According to the post How does the Windows Command Interpreter (CMD.EXE) parse scripts? (see phase 5), the line echo !!CLs[!count!]!! cannot work, because the opening !! are collapsed to a single !, then !CLs[! is expanded to an empty string (assuming such variable is not defined), then count is returned literally, then !]! is expanded to an empty string and the final ! is dismissed. Or in other words, delayed expansion cannot be nested.
You can use call though to introduce another parsing phase, like this:
call echo %%CLs[!count!]%%
The line IF NOT %%I == CLs[!count!] ( ... ) is wrong, you must expand the right value too. However, call if will not help unfortunately, because if (like for and rem) is a special command that is recognised by the parser earlier than others, like call.
To work around that you can store the value of !count! in a for meta-variable, like %%J, for instance, to introduce another parsing phase, and use !CLs[%%J]! then, like this:
set /A "count=0"
for /F "tokens=5" %%I in ('some command') do (
for %%J in (!count!) do (
echo !CLs[%%J]!
if not "%%I" == "!CLs[%%J]!" (
set /A "count+=1"
set "CLs[!count!]=%%I"
)
)
)
Another yet slower possibility is to put the relevant code into a sub-routine:
set /A "count=0"
for /F "tokens=5" %%I in ('some command') do (
call :SUB !count!
)
goto :EOF
:SUB
echo !CLs[%~1]!
if not "%%I" == "!CLs[%~1]!" (
set /A "count+=1"
set "CLs[%~1]=%%I"
)
goto :EOF
You may also take a look at the post Arrays, linked lists and other data structures in cmd.exe (batch) script about how to deal with such pseudo-arrays.
ECHO ------------- START AT %time%
REM <!-- language: lang-dos -->
#ECHO Off
setlocal enableextensions ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q58209698.txt"
SET CLs[0]=#
SET /a clscnt[0]=0
SET /A count = 0
FOR /F "tokens=*" %%I IN ('type %filename1%') DO (
SET "processed="
FOR /f "tokens=1,2,3delims=[]=" %%a IN ('set cls[') DO IF /i "%%a"=="cls" (
IF "%%I"=="%%c" (SET /a clscnt[%%b]+=1&SET "processed=y")
)
IF not DEFINED processed SET /a count+=1&SET "cls[!count!]=%%I"&SET /a clscnt[!count!]=1
)
FOR /L %%a IN (0,1,%count%) DO ECHO !clscnt[%%a]! times !cls[%%a]!
ENDLOCAL
ECHO -------------------------Second way -----------------
#ECHO Off
setlocal enableextensions ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q58209698.txt"
SET CLs[0]=#
SET /a clscnt[0]=0
SET /A count = 0
FOR /F "tokens=*" %%I IN ('type %filename1%') DO (
SET "processed="
FOR /L %%a IN (0,1,!count!) DO (
IF "%%I"=="!cls[%%a]!" (SET /a clscnt[%%a]+=1&SET "processed=y")
)
IF not DEFINED processed SET /a count+=1&SET "cls[!count!]=%%I"&SET /a clscnt[!count!]=1
)
FOR /L %%a IN (0,1,%count%) DO ECHO !clscnt[%%a]! times !cls[%%a]!
ENDLOCAL
GOTO :EOF
I used a file named q58209698.txt containing some dummy data for my testing and chose to use the entire data line, having no suitable files where token 5 existed.
Note that as a bonus, I've added clscnt - an array of occurence-counts.
Shown: two separate ways of achieving the aim of finding/counting the unique tokens. Naturally, if the cls array is pre-loaded with the required tokens, then it's basic-programmer's-play to adjust the code to detect/report occurrences of those tokens.
The two methods are similar. In the first, set is used to list the established variables starting cls[. The first if ensures processing only the array-name cls, then either it's a repeat (set prcoessed to a value and increment the occurrences-counter) or it's a new value (when the for...%%a loop ends, processed is still undefined) so record it.
The second way is more direct, using the value of count to specifically interrogate the values in the cls array.

Extract a specific portion from a string(filename) using batch file

I am trying to extract a portion of all the filenames(pdf files) in the current directory.
The length of filenames vary except for the last portion(datetime and extension) which will always be 16 characters. The remaining part will always have different lengths. Even the portion I require may have varying lengths.
I tried using lastIndexOf function obtained here.
filename eg : academyo-nonpo-2582365-082416051750.pdf
I want to extract the section in Bold.
I tried trimming the last 17 characters(this portion will always have a fixed length.) first and then tried to obtain the last Index Of '-'(since the fist portion can have variable character length.) and trim the characters until that position, which should return the required portion of the filename.
#echo off
Setlocal enabledelayedexpansion
For %%# in ("%~dp0\*.pdf") Do (
Set "File=%%~nx#"
Set "File=!File:~0,-17!"
Set "lio2="
#echo on
echo !File!
#echo off
call :lastindexof !File! - lio2
Set "File=!File:~%lio%!"
)
Pause&Exit
:lastindexof [%1 - string ; %2 - find last index of ; %3 - if defined will store the result in variable with same name]
#echo off
setlocal enableDelayedExpansion
set "str=%~1"
set "p=!str:%~2=&echo.!"
set "splitter=%~2"
set LF=^
rem ** Two empty lines are required
echo off
for %%L in ("!LF!") DO (
for /f "delims=" %%R in ("!splitter!") do (
set "var=!str:%%R=%%L!"
)
)
for /f delims^=^" %%P in ("!var!") DO (
set "last_part=%%~P"
)
if "!last_part!" equ "" if "%~3" NEQ "" (
echo "not contained" >2
endlocal
set %~3=-1
exit
) else (
echo "not contained" >2
endlocal
set argv=original
set $strLen=for /L %%n in (1 1 2) do if %%n==2 (%\n%
for /F "tokens=1,2 delims=, " %%1 in ("!argv!") do (%\n%
set "str=A!%%~2!"%\n%
echo -1
)
setlocal DisableDelayedExpansion
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
set "len=0"%\n%
for /l %%A in (12,-1,0) do (%\n%
set /a "len|=1<<%%A"%\n%
for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A"%\n%
)%\n%
for %%v in (!len!) do endlocal^&if "%%~b" neq "" (set "%%~1=%%v") else echo %%v%\n%
) %\n%
) ELSE setlocal enableDelayedExpansion ^& set argv=,
%$strlen% strlen,str
%$strlen% plen,last_part
%$strlen% slen,splitter
set /a lio=strlen-plen-slen
endlocal & if "%~3" NEQ "" (set %~3=%lio%) else echo %lio%
exit /b
The reference of the variable passed to the function as the 3rd parameter doesn't seem to be returning the required value.
I dunno what is wrong here.
To get the section in bold then:
Example#
#Echo Off
SetLocal EnableDelayedExpansion
For %%# in ("%~dp0*.pdf") Do (
Set "File=%%~n#"
Set "File=!File:~-20,7!"
Echo=!File!%%~x#)
Pause
Okay what about?
#Echo Off
SetLocal EnableDelayedExpansion
For %%# in ("%~dp0*.pdf") Do (
Set "File=%%~n#"
Set "File=!File:~,-13!"
Call :Sub "!File:-=\!%%~x#")
Pause
:Sub
Echo=%~nx1
To extract the portion in between the last hyphen and the next-to-last one, you could use the following script (provide the strings/files as command line arguments):
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "SEP=-"
for %%A in (%*) do (
set "ITEM=%%~A"
set "PREV="
if defined ITEM (
for %%B in ("!ITEM:%SEP%=" "!") do (
set "PREV=!PART!"
set "PART=%%~B"
)
if defined PREV (
echo(!PREV!
)
)
)
endlocal
exit /B
This approach basically replaces every - by the standard cmd tokenisation character SPACE and iterates through the resulting string using a standard for loop (no /F option). The currently iterated part is stored in variable PART, whose content is first copied into PREV to gain a delay of one loop iteration. So the next-to-last portion is finally stored in PREV.
Note that this script might return unexpected results in case the strings/files contain exclamation marks because of delayed expansion.
Have a look on this answer. Thought is to first count the number of tokens (you still do have to trim the string before this) and then get the last token.
In the first loop where it says "tokens=1*" , you have to edit it to the following: "tokens=1* delims=-" and in the second loop add delims=- as well after %i%. It should be looking like this in total with your script:
#echo off
SetLocal EnableDelayedExpansion
For %%# in ("%~dp0\*.pdf") Do (
Set "File=%%~nx#"
Set "File=!File:~0,-17!"
Set "lio2="
#echo on
echo !File!
#echo off
call:subfunction !File! - lio2
Set "File=!File:~%lio%!"
)
:subfunction
set var1=%1
set var2=%var1%
set i=0
:loopprocess
for /F "tokens=1* delims=-" %%A in ( "%var1%" ) do (
set /A i+=1
set var1=%%B
goto loopprocess )
for /F "tokens=%i% delims=-" %%G in ( "%var2%" ) do set last=%%G
echo %last%
REM do what you want with last here!
I tested it and it seems to be working correctly even with something like ac-ade-myo-n-on-po-15482729242321654-082416051750.pdf, however after finishing correctly, it give an error message one time with a syntax error I could not find...
If you can ignore that error (everything else works), this might help.

how to read second to second last word from a string in a batch file

Suppose my string is "Hello;world!How;are;you;doing?". How do I read 2nd word to second last word in a loop.
I have tried the following, but ended with errors
set argC=0
FOR /f %%x IN ("Hello;world!How;are;you;doing?") DO (
SET /A argC+=1
)
set /a last2last=!argC!-1
for /f "usebackq tokens=2-!argC! delims=;" %%y in ("Hello;world!How;are;you;doing?") do (
set "somestr=!somestr! %%y "
)
echo !somestr!
I am getting the error "!last2last! delims=;" was unexpected at this time."
Note: Number of words in the string may vary
Can anyone help me?
Based on the actual data in the comments - this works.
#echo off
setlocal enabledelayedexpansion
FOR %%x IN ("&&";"XMLWrirter";"class";"free";"&&") DO (
if defined read SET a=!a! !b!& set b=%%x
set read=1
)
echo !a:~2!
pause
Try this:
#echo off
setlocal EnableDelayedExpansion
set "all="
set "this="
for %%a in ("&&";"XMLWrirter";"class";"free";"&&") do (
set "all=!all!;!this!"
if "!all!" neq ";" set "this=%%a"
)
echo !all:~3!
The output is "XMLWrirter";"class";"free", but the semicolons may be changed by spaces, if you wish, and the quotes may be deleted.

Loop through "array" in batch file to shift elements

I have a batch file that is passed commands in the form of a string array from a Java file. The commands contain something like the following:
String[] commands = {"A",
"B",
"C",
"C:\users\user\Documents",
"C:\users\user\Pictures"}
The commands array is dynamic, as it changes every time the java program is run. In the batch file, I create variables to take the values of the first three elements (A, B, and C in this case). Then I need to shift the directory strings to take up the first three elements of the array. Here is the batch code I have so far:
#echo off
setlocal enableDelayedExpansion
set /A paramCount=0
for %%x in (%*) do (
set list[!paramCount!]=%%x
set /A paramCount=paramCount+1
)
set argA=%list[0]%
set argB=%list[1]%
set argC=%list[2]%
set /A old=0
set /A new=!old!+3
for /F "tokens=2 delims==" %%a in ('set list[') do (
echo old=!old!
echo new=!new!
set list[!old!]=!list[%new%]!
echo !list[%old%]!
set /A old=!old!+1
set /A new=!new!+1 )
The problem I am having is with the line set list[!old!]=!list[%new%]!. As you can see, I have delayed expansion enabled. However, the !!'s are needed for the list[...] variable that is emulating an element in an array. However, I believe I need to use delayed expansion for "new" as well. What am I to do in this case? Or perhaps that's not the actual problem? The "old" and "new" variables are incrementing correctly, but the echo !list[%old%]! line returns the same value every time. I expect the same issue exists in that line, with "old"--It should have !'s surrounding it but the !'s are already being used for the list[...] variable. So what happens if you need nested !'s in a statement? Thanks for the aid!
#echo off
setlocal ENABLEDELAYEDEXPANSION
set /A paramCount=-3
for %%x in (%*) do (
set list[!paramCount!]=%%x
set /A paramCount=paramCount+1
)
set argA=%list[-3]%
set argB=%list[-2]%
set argC=%list[-1]%
for /F "tokens=2 delims==" %%a in ('set list[-') do SET "%%a="
SET arg
SET list
ENDLOCAL
echo==================
setlocal ENABLEDELAYEDEXPANSION
set /A paramCount=0
for %%x in (%*) do (
set list[!paramCount!]=%%x
set /A paramCount=paramCount+1
)
set argA=%list[0]%
set argB=%list[1]%
set argC=%list[2]%
set /A old=0
set /A new=!old!+3
for /F "tokens=2 delims==" %%a in ('set list[') do (
echo old=!old!
echo new=!new!
CALL set list[%%old%%]=%%list[!new!]%%
CALL ECHO(%%list[!old!]%%
set /A old=!old!+1
set /A new=!new!+1
)
SET arg
SET list
GOTO :EOF
This should work for you - the easy way and the hard way.

Resources