How to set a variable in these batch loops? - batch-file

think I'm getting things confused here.
I've got a loop that runs all files in a folder
for /f "delims=_" %%J in ('forfiles /p "%%F" /m *.ext /c "cmd /c echo #path"')
do start "program" /D "c:\program files\path\to\program" /Wait program -r %%J
%%J should represent each file if I've set this up / interpretted this correctly.
I have another loop that is looking in the xml code for each of these files and searching for a particular pattern using findstr and parsing out the Name from some tags like this:
for /f "tokens=3 delims=<>" %%a in ('findstr /n /i "<Name>ABCDir" "%%J"')
do (set name=%%a)
echo !name!
Now I thought it would be as easy as just reusing %%J in the findstr loop but it doesn't seem to be working. When I run the code, it tells me FINDSTR: Cannot open %J and then ECHO is off
I'm guessing my problem is that it was too quick and easy to try using %%J in the next loop and that the shell isn't connecting the dots between loops.
Any ideas how I can do this? Because I need the file name in the findstr loop to always match the file in the first loop.
EDIT: Here's what the file might look like.
c:\path\to\the file name
Here's what the output looks like:
FINDSTR: Cannot open "c:\path\
FINDSTR: Cannot open to\
FINDSTR: Cannot open the
FINDSTR: Cannot open file
FINDSTR: Cannot open name
so it would seem its a simple issue of how the shell is reading the %%J variable. This type of thing has shown up when I've forgotten to put quotes around file names before but the quotations are around %%J. I even tried double quotations but was a little relieved when that didn't fix it.
EDIT2: I changed
for /f "tokens=3 delims=<>" %%a in ('findstr /n /i "<Name>ABCDir" "%%J"')
do (set name=%%a)
to
for /f "tokens=3 delims=<>" %%a in ('findstr /n /i "<Name>ABCDir" "%%~nJ"')
do (set name=%%a)
and now the output is: FINDSTR: Cannot open the file name. So now at least its reading the file in full. At least it would seem that way.

for %%J in ( ... ) do (
....
%%J is visible here, inside the do clause
....
) <- here %%J goes out of scope
So, you can include your second (third : %%F?) for loop inside the do clause of the first one
for %%J in ("%%F\*.ext") do (
start "program" /D "c:\program files\path\to\program" /Wait program -r "%%~fJ"
for /f "tokens=3 delims=<>" %%a in ('findstr /n /i "<Name>ABCDir" "%%J"') do (
echo %%a
)
)

Related

Findstr within a ("variable")folder

I am pretty new at programming in batch files, and stack overflow users have being a lot of help. My problem is almost solved. Now there is only one thing that is missing to make my script work is.
for /f "tokens=1,* delims=: " %%i in ('type "C:\dev\1597\AssayInfo.txt" ^| findstr /i CouID') do set "number=%%j"
echo %number%
in this part of the code i need to be able to find the AssayInfo.txt without the folder 1597. In my case i will have a lot of folders with random generated numbers and all of them have a Assayinfo.txt, but if i try to run the code without the 1597 path it just say that could not find the file.
We kind of went through these already, but anyway:
To actually set it as a variable after file was found.:
#echo off
setlocal enabledelayedexpansion
for /f %%i in ('dir /s /b /a-d AssayInfo.txt') do (
for /f "tokens=2" %%a in ('type "%%~fi" ^| findstr /i "CouID"') do set "number=%%a"
echo Found number !number! in file "%%~fi"
)

Batch file to get count of .mea file

I have a batch file which will give the file name & number of lines in that file. It works properly for .txt files, but I am trying to check count of a .mea file, it shows incorrect count. (It shows very large count). below is the code,please suggest any corrections to read .mea file correctly:
#echo off
(
for /R %%n IN (*.*) do (
for /F "tokens=1,*" %%f in ('find /V /C "-------------" "%%n"') do (
echo %%n : %%g
)
)) >output.txt
Most likely your .mea file is a binary type file where the line terminators as understood by Find are not considered as terminators as understood by you. (…without a genuine .mea file to examine we cannot help you verify that)
I think however that you could at least further improve your code:
…single directory
#Echo Off
( For /F "Tokens=1* Delims=- " %%A In ('Find/V /C "" *.* 2^>Nul'
) Do Echo=%%A %%B)>"SomewhereElse\Output.txt"
…recursive
#Echo Off
(For /R %%A In (*) Do For /F "Tokens=1* Delims=- " %%B In (
'Find/V /C "" "%%A" 2^>Nul') Do Echo=%%B %%C)>"SomewhereElse\Output.txt"
In both cases, unless Output.txt is written to SomewhereElse, it will show up as an entry within its own content.

Multistep console inputs for a command in Batch File

I have 10 text files in a folder and a jar file which will take three inputs one by one.
Text file path
Vendor Name
Model
I want to execute the Jar file 10 times (num of text files). I have a code that will execute 10 times and feed the first input "text file path"
#echo off
for %%a in (*.txt) do (
echo %%a|java -jar C:\Generator.jar
)
pause
I want to give the remaining two "Vendor name and Model" as console inputs one by one automatically. The vendor name and model should be populated by splitting text file name using underscore. Is there a way to achieve this ?
You can use for /f to split the file name into two tokens at the _ delimiter. You don't want to include the .txt extension, so you need to split %%~nA instead of %%A.
You could use dir with findstr to verify that the file name has valid format, and change the outer for loop to for /f to process that result. For example, you don't want to process a file name that looks like "vendor_.txt". My findstr command verifies there is only one _ and there is at least one non-underscore character both before and after.
One would think that the following would work
#echo off
:: This does not work - each line of input to java has an unwanted trailing space
for /f "delims=" %%A in (
'dir /a-d /b *_*.txt ^| findstr /i "^[^_][^_]*_[^_][^_]*\.txt$"'
) do for /f "delims=_ tokens=1*" %%B in ("%%~nA") do (
echo %%A
echo %%B
echo %%C
) | java -jar c:\generator.jar
But there is an insidious problem due to the fact that each side of the pipe is executed in a new cmd.exe process. Each side of the pipe must be parsed and packaged up into a cmd /c .... command. The left side ends up looking something like:
C:\Windows\system32\cmd.exe /S /D /C"echo fileName &echo vendor &echo model ",
with an unwanted space at the end of each line. Adding parentheses like (echo %%A) does not help.
There is another potential problem if any of the file names contain a poison character like &. If your vendor name is "Bell&Howell", then the left side process would attempt to execute
echo Bell&Howell. The "Bell" would echo properly, but the "Howell" would be interpreted as a command and would almost surely fail with an error, but in any case would not give the desired result.
See Why does delayed expansion fail when inside a piped block of code? for more information about potential issues with pipes, along with strategies for dealing with them.
Below is an enhancement to my original code that should fix the problems. I solve the poison character issue by putting the string literals within quotes, and then use a for statement that executes within the child cmd.exe process to safely strip the quotes before ECHOing the value.
I solve the unwanted trailing space issue by delaying parsing of the command by using an %%echoX%% "macro". The double percents delays expansion of the "macro" until after we are in the child process.
#echo off
setlocal disableDelayedExpansion
:: This echoX "macro" delays parsing of the echo command to avoid the unwanted trailing space
set "echoX=(echo %%~X)"
for /f "delims=" %%A in (
'dir /a-d /b *_*.txt ^| findstr /i "^[^_][^_]*_[^_][^_]*\.txt$"'
) do for /f "delims=_" tokens=1*" %%B in ("%%~nA") do (
(for %%X in ("%%~A" "%%B" "%%C") do #%%echoX%%) | java -jar c:\generator.jar
)
Another option is to define a multi-line variable with embedded linefeeds, and then use CMD /V:ON to enable delayed expansion in the child process.
#echo off
setlocal disableDelayedExpansion
for %%L in (^"^
%= This creates a quoted linefeed character in the FOR variable L =%
^") do for /f "delims=" %%A in (
'dir /a-d /b *_*.txt ^| findstr /i "^[^_][^_]*_[^_][^_]*\.txt$"'
) do for /f "delims=_" tokens=1*" %%B in ("%%~nA") do (
set "str=%%A%%~L%%B%%~L%%C"
cmd /v:on /c "(echo !str!)" | java -jar c:\generator.jar
)
Here is a simpler alternative that uses a temporary file instead of a pipe. It avoids the many issues that can arise with pipes.
#echo off
for /f "delims=" %%A in (
'dir /a-d /b *_*.txt ^| findstr /i "^[^_][^_]*_[^_][^_]*\.txt$"'
) do for /f "delims=_" tokens=1*" %%B in ("%%~nA") do (
>temp.txt (
(echo %%~A)
(echo %%B)
(echo %%C)
)
<temp.txt java -jar c:\generator.jar
del temp.txt
)

Batch FOR /F loop won't read relative directory

I have a simply FOR /F loop which strips out all but one line of a text file:
for /f "skip=12 tokens=* delims= " %%f in (.\NonProcessed\*.txt) do (
> newfile.txt echo.%%f
goto :eof
)
But when I run, I get the result:
The system cannot find the file .\NonProcessed\*.txt
The for loop works fine if I enter a fully qualified path to the text file within the brackets, but it can't handle the relative link I have in there. I've been able to use the exact same relative link in another standard for loop in a different batch file running in the same directory without any issues. I can't understand why it won't work! Please help.
EDIT: For comments, code I'm using now is
for %%f in (.\NonProcessed\*.txt) do (
echo f is %%f
for /f "usebackq skip=12 tokens=* delims= " %%a in (%%f) do (
echo a is %%a
> %%f echo.%%a
goto :continue
)
:continue
sqlcmd stuff here
)
Sorry but for /f does not allow you to do that. And no, the problem is not the relative path to files but the wildcard.
According to documentation, you have the syntax case
for /F ["ParsingKeywords"] {%% | %}variable in (filenameset) do command [CommandLineOptions]
For this case, documentation states The Set argument specifies one or more file names. You can do
for /f %%a in (file1.txt file2.txt file3.txt) do ...
but wildcards are not allowed.
If you don't know the name of the file you want to process, your best option is to add an additional for command to first select the file
for %%a in (".\NonProcessed\*.txt"
) do for /f "usebackq skip=12 tokens=* delims= " %%f in ("%%~fa"
) do (
> newfile.txt echo(%%f
goto :eof
)
When executed, the goto command will cancel both for loops so you end with the same behaviour you expected from your original code.
edited to adapt code to comments
#echo off
set "folder=.\NonProcessed"
pushd "%folder%"
for /f "tokens=1,2,* delims=:" %%a in (
' findstr /n "^" *.txt ^| findstr /r /b /c:"[^:]*:13:" '
) do (
echo Overwrite file "%%a" with content "%%c"
>"%%a" echo(%%c
)
popd
Read all the files in the folder, numbering the lines. The output for the first findstr command will be
filename.txt:99:lineContents
This output is parsed to find the line 13, the resulting data is splitted using the colon as a separator, so we will end with the file name in %%a, the line number in %%b and the line content in %%c.
SET FILES_LIST=files_list.config
DIR /b .\NonProcessed\*.txt>!FILES_LIST!
for /f "skip=12 tokens=* delims= " %%f in (!FILES_LIST!) do (
> newfile.txt echo.%%f
goto :eof
)
IF EXIST "!FILES_LIST!" DEL "!FILES_LIST!"
I did not check how your's FOR works, just added my additions/corrections to it.... Hope it will work for you.
Best regards!

DOS batch FOR loop with FIND.exe is stripping out blank lines?

This DOS batch script is stripping out the blank lines and not showing the blank lines in the file even though I am using the TYPE.exe command to convert the file to make sure the file is ASCII so that the FIND command is compatible with the file. Can anyone tell me how to make this script include blank lines?
#ECHO off
FOR /F "USEBACKQ tokens=*" %%A IN (`TYPE.exe "build.properties" ^| FIND.exe /V ""`) DO (
ECHO --%%A--
)
pause
That is the designed behavior of FOR /F - it never returns blank lines. The work around is to use FIND or FINDSTR to prefix the line with the line number. If you can guarantee no lines start with the line number delimiter, then you simply set the appropriate delimiter and keep tokens 1* but use only the 2nd token.
::preserve blank lines using FIND, assume no line starts with ]
::long lines are truncated
for /f "tokens=1* delims=]" %%A in ('type "file.txt" ^| find /n /v ""') do echo %%B
::preserve blank lines using FINDSTR, assume no line starts with :
::long lines > 8191 bytes are lost
for /f "tokens=1* delims=:" %%A in ('type "file.txt" ^| findstr /n "^"') do echo %%B
::FINDSTR variant that preserves long lines
type "file.txt" > "file.txt.tmp"
for /f "tokens=1* delims=:" %%A in ('findstr /n "^" "file.txt.tmp"') do echo %%B
del "file.txt.tmp"
I prefer FINDSTR - it is more reliable. For example, FIND can truncate long lines - FINDSTR does not as long as it reads directly from a file. FINDSTR does drop long lines when reading from stdin via pipe or redirection.
If the file may contain lines that start with the delimiter, then you need to preserve the entire line with the line number prefix, and then use search and replace to remove the line prefix. You probably want delayed expansion off when transferring the %%A to an environment variable, otherwise any ! will be corrupted. But later within the loop you need delayed expansion to do the search and replace.
::preserve blank lines using FIND, even if a line may start with ]
::long lines are truncated
for /f "delims=" %%A in ('type "file.txt" ^| find /n /v ""') do (
set "ln=%%A"
setlocal enableDelayedExpansion
set "ln=!ln:*]=!"
echo(!ln!
endlocal
)
::preserve blank lines using FINDSTR, even if a line may start with :
::long lines >8191 bytes are truncated
for /f "delims=*" %%A in ('type "file.txt" ^| findstr /n "^"') do (
set "ln=%%A"
setlocal enableDelayedExpansion
set "ln=!ln:*:=!"
echo(!ln!
endlocal
)
::FINDSTR variant that preserves long lines
type "file.txt" >"file.txt.tmp"
for /f "delims=*" %%A in ('findstr /n "^" "file.txt.tmp"') do (
set "ln=%%A"
setlocal enableDelayedExpansion
set "ln=!ln:*:=!"
echo(!ln!
endlocal
)
del "file.txt.tmp"
If you don't need to worry about converting the file to ASCII, then it is more efficient to drop the pipe and let FIND or FINDSTR open the file specified as an argument, or via redirection.
There is another work around that completely bypasses FOR /F during the read process. It looks odd, but it is more efficient. There are no restrictions with using delayed expansion, but unfortunately it has other limitations.
1) lines must be terminated by <CR><LF> (this will not be a problem if you do the TYPE file conversion)
2) lines must be <= 1021 bytes long (disregarding the <CR><LF>)
3) any trailing control characters are stripped from each line.
4) it must read from a file - you can't use a pipe. So in your case you will need to use a temp file to do your to ASCII conversion.
setlocal enableDelayedExpansion
type "file.txt">"file.txt.tmp"
for /f %%N in ('find /c /v "" ^<"file.txt.tmp"') do set cnt=%%N
<"file.txt.tmp" (
for /l %%N in (1 1 %cnt%) do(
set "ln="
set /p "ln="
echo(!ln!
)
)
del "file.txt.tmp"
I wrote a very simple program that may serve as replacement for FIND and FINDSTR commands when they are used for this purpose. My program is called PIPE.COM and it just insert a blank space in empty lines, so all the lines may be directly processed by FOR command with no further adjustments (as long as the inserted space don't cares). Here it is:
#ECHO off
if not exist pipe.com call :DefinePipe
FOR /F "USEBACKQ delims=" %%A IN (`pipe ^< "build.properties"`) DO (
ECHO(--%%A--
)
pause
goto :EOF
:DefinePipe
setlocal DisableDelayedExpansion
set pipe=´)€ì!Í!ŠÐŠà€Ä!€ü.t2€ü+u!:æu8²A€ê!´#€ì!Í!².€ê!´#€ì!Í!²+€ê!´#€ì!Í!Šò€Æ!´,€ì!Í!"Àu°´LÍ!ëÒ
setlocal EnableDelayedExpansion
echo !pipe!>pipe.com
exit /B
EDIT: Addendum as answer to new comment
The code at :DefinePipe subroutine create a 88 bytes program called pipe.com, that basically do a process equivalent to this pseudo-Batch code:
set "space= "
set line=
:nextChar
rem Read just ONE character
set /PC char=
if %char% neq %NewLine% (
rem Join new char to current line
set line=%line%%char%
) else (
rem End of line detected
if defined line (
rem Show current line
echo %line%
set line=
) else (
rem Empty line: change it by one space
echo %space%
)
)
goto nextChar
This way, empty lines in the input file are changed by lines with one space, so FOR /F command not longer omit they. This works "as long as the inserted space don't cares" as I said in my answer.
Note that the pipe.com program does not work in 64-bits Windows versions.
Antonio
Output lines including blank lines
Here's a method I developed for my own use.
Save the code as a batch file say, SHOWALL.BAT and pass the source file as a command line parameter.
Output can be redirected or piped.
#echo off
for /f "tokens=1,* delims=]" %%a in ('find /n /v "" ^< "%~1"') do echo.%%ba
exit /b
EXAMPLES:
showall source.txt
showall source.txt >destination.txt
showall source.txt | FIND "string"
An oddity is the inclusion of the '^<' (redirection) as opposed to just doing the following:
for /f "tokens=1,* delims=]" %%a in ('find /n /v "" "%~1"') do echo.%%ba
By omitting the redirection, a leading blank line is output.
Thanks to dbenham, this works, although it is slightly different than his suggestion:
::preserve blank lines using FIND, no limitations
for /f "USEBACKQ delims=" %%A in (`type "file.properties" ^| find /V /N ""`) do (
set "ln=%%A"
setlocal enableDelayedExpansion
set "ln=!ln:*]=!"
echo(!ln!
endlocal
)
As mentioned in this answer to the above question, it doesn't seem that lines are skipped by default using for /f in (at least) Windows XP (Community - Please update this answer by testing the below batch commands on your version & service pack of Windows).
EDIT: Per Jeb's comment below, it seems that the ping command, in at least Windows XP, is
causing for /f to produce <CR>'s instead of blank lines (If someone knows specifically why, would
appreciate it if they could update this answer or comment).
As a workaround, it seems that the second default delimited token (<space> / %%b in the example)
returns as blank, which worked for my situation of eliminating the blank lines by way of an "parent"
if conditional on the second token at the start of the for /f, like this:
for /f "tokens=1,2*" %%a in ('ping -n 1 google.com') do (
if not "x%%b"=="x" (
{do things with non-blank lines}
)
)
Using the below code:
#echo off
systeminfo | findstr /b /c:"OS Name" /c:"OS Version"
echo.&echo.
ping -n 1 google.com
echo.&echo.
for /f %%a in ('ping -n 1 google.com') do ( echo "%%a" )
echo.&echo.&echo --------------&echo.&echo.
find /?
echo.&echo.
for /f %%a in ('find /?') do ( echo "%%a" )
echo.&echo.
pause
.... the following is what I see on Windows XP, Windows 7 and Windows 2008, being the only three versions & service packs of Windows I have ready access to:

Resources