I have written below script:
#echo off
setlocal EnableDelayedExpansion
REM Collect source filenames from C:\Files and load into C:\doc.txt
dir C:\sources\Sourcefiles /b /a-d > C:\sourcefilenames.txt
REM fetch count of source files and store into variable count
For /F %%I in ('call C:\count.bat ') Do Set count=%%I
REM loop "count" number of times and echo temp.txt value
FOR /L %%A IN (1,1,%count%) DO (
REM call line.bat to fetch line 1,line 2 and so on of sourcefilenames.txt for each loop
call line.bat %%A>C:\temp.txt
set /p var=<C:\temp.txt
echo var:%var% ----------> returns previous run value
type C:\temp.txt ----------. returns current value of temp.txt
)
Basically what i am trying to do out of the above script is:
I am creating a variable(var) from the content of temp.txt(data in temp.txt will change for each time loop runs) to be used in multiple loops.
But the problem i am facing is :
Echo var is:%var% command returning me previous run value not temp.txt current content.whereas command "type C:\temp.txt" returning me temp.txt current content.
(Note: if i have called/created variable "var" from some other script it returns me that previous value else it returns Null)
Your help/guidance on above issue is really appreciated.
Thanks
I suppose the variable remains in memory, without being re-read.Attempts to limit the validity of the variable.
setlocal
echo something.....
endlocal
or #echo off & setlocal
When CMD.exe encounters a block of code in parentheses, it reads and parses the entire block before executing. This can cause unintuitive behavior. In this case, your echo var:%var% line is being parsed once at the beginning of the loop and never again.
The easiest fix for this is to change that line to
echo var:!var!
The !var! syntax is parsed every time through the loop. This works because you have enabledelayedexpansion set in your script.
Another workaround to this type of problem is to remove the parentheses and instead call out to a subroutine.
FOR /L %%A IN (1,1,%count%) DO call :loopLineBat %%A
... rest of script
exit /b
:loopLineBat
>%temp%\temp.txt call line.bat %1
<%temp%\temp.txt set /p var=
echo var:%var%
type %temp%\temp.txt
exit /b
This loop does the same as above, but because it is not in a parenthesized block, all of the lines are parsed and executed in order.
Related
Although I'm really a newbie in this field, I want to accomplish a task in batch scripting: There is a determinate folder of company contracts in a determinate path, each of this folders (approx. 400) has a common folder (2016) where there might be a file indicating there has been an inspection in this year. What i want is to print every company folder that has not any file in the common 2016 folder and a count of the times this happens.
This is what i have (and does not work at all):
set c=0
for %i /d in (*) do
for %j in ($%i\2016\*) do
if (%j==NUL) then (#echo $%i c+=1 echo %c)`
If you just want to know if there is a file in the 2016 directory you can do this:
#echo off
SetLocal EnableDelayedExpansion
set count=0
for %%i /d in (*) do (
REM first unset variable
set files=
for %%j in (%%i\2016\*) do (
REM will set variable each time a file is encountered
set files=present
)
if not DEFINED files (
REM No files in directory 2016
echo %%i
set /a count+=1
echo !count!
)
)
EndLocal
exit /b 0
I don't see why you use $ before each %i. If you execute this code from the command line use one % for the loop variables i and j. But in a batch-script you'll have to use two of them (%%i, %%j).
Another thing, c+=1 won't work except if you use set /a.
I used delayed expansion because each block code ( between (...)) is parsed as one single command (as if it was all on one line with && between the commands inside the block) and you can't just assign a new value to a variable and read that new value in the same command. That's also the reason why I use !count! instead of %count% (which will give the value before the block). If you'd rather not use delayed expansion, remove the SetLocal EnableDelayedExpansion and replace echo !count! with call echo %%count%% (is another way to read a new value in the same command)
Also, be aware that each echo will end its output with a carriage retur and a newline. So each echo will result in a new line of output.
I have a question how to set multiple files as variables in batch file? I trying to do something with below script:
move c:\*.gpg q:\
for %%F in ("q:\*.gpg") do (set file=%%~nxF)
if exist q:\*.gpg echo done copying files: %file%
Above works fine only with one file. If there are more than two it only echos one of them. How can I echo in one line all the files that where copied?
There are multiple problems with your code
for %%F in ("q:\*.gpg") do (set file=%%~nxF)
if exist q:\*.gpg echo done copying files: %file%
First, you're assigning new values to the variable file each loop and then overwriting it every time without doing anything. Therefore after the loop file will only contain the value in the last loop. Move the echo part to inside the loop instead
Another problem is that variables are expanded at parse time by default. You need to enable delayed expansion and use ! instead of % to make it expand at runtime
Setlocal EnableDelayedExpansion
for %%F in ("q:\*.gpg") do (
set file=%%~nxF
echo done copying file: !file!
)
If you want to echo all files at once then set the file variable to contain the list of files
for %%F in ("q:\*.gpg") do (
set file=!file!, %%~nxF
)
echo done copying files: %file%
The file existence check is redundant. You can check the exit code after moving and exit if the files weren't moved.
The variables are not constant so remember to activate the expansion delayed variable, here the expansion of the variable file must be delayed.
Delayed Expansion will cause variables to be expanded at execution time rather than at parse time, this option is turned on with the SETLOCAL command. When delayed expansion is in effect variables can be referenced using !variable_name! (in addition to the normal %variable_name% )
Delayed variable expansion is often useful when working with FOR Loops, normally an entire FOR loop is evaluated as a single command even if it spans multiple lines of a batch script.
This is the default behaviour of a FOR loop:
Example :
#echo off
setlocal
:: count to 5 storing the results in a variable
set _tst=0
FOR /l %%G in (1,1,5) Do (echo [%_tst%] & set /a _tst+=1)
echo Total = %_tst%
C:\> demo_batch.cmd
[0]
[0]
[0]
[0]
[0]
Total = 5
Notice that when the FOR loop finishes we get the correct total, so the variable correctly increments, but during each iteration of the loop
the variable is stuck at it's initial value of 0
The same script with EnableDelayedExpansion, gives the same final result but also displays the intermediate values:
#echo off
setlocal EnableDelayedExpansion
:: count to 5 storing the results in a variable
set _tst=0
FOR /l %%G in (1,1,5) Do (echo [!_tst!] & set /a _tst+=1)
echo Total = %_tst%
C:\> demo_batch.cmd
[0]
[1]
[2]
[3]
[4]
Total = 5
Notice that within the for loop we use !variable! instead of %variable%.
And your batch script can be written like that :
#echo off
Set "Ext=*.gpg"
set /a Count=0
Setlocal EnableDelayedExpansion
for %%F in ("q:\%Ext%") do (
SET /a Count+=1
set "file=%%~nxF"
echo Done copying file [!Count!] : !file!
)
SET /a "COUNT_TOT=%Count%"
ECHO.
ECHO Total of [%EXT%] files(s) : %Count% file(s)
pause
I have this file structure where each subdirectory contains particular file types, lets say pdfs.
ParentDIR
-->SUBDIR1
-->SUBDIR2
I am trying to run a batch file from the parent to recursively return paths so I can parse them and perform some action
#echo off
for /R %%v in (*.pdf) do (
set pathname=%%~pv
echo %pathname%
)
I would expect the output of the path variable here to read
\PARENTDIR\SUBDIR1
\PARENTDIR\SUBDIR2
but it reads
\PARENTDIR\SUBDIR2
\PARENTDIR\SUBDIR2
If I echo the value of %%~pv without assigning it to a variable, it is correct.
How can I get the value of this variable to be assigned correctly at each iteration through the loop?
#echo off >Nul
setlocal EnableDelayedExpansion
for /R %%v in (*.pdf) do (
set pathname=%%~pv >Nul
echo !pathname!
)
endlocal
Here mind EnableDelayedExpansion keyword of setlocal command = Expand variables at execution time rather than at parse time, if used !pathname! syntax instead of %pathname% one.
I am trying to write a batch file that reads each line in a text file and assigns it (each line) to a variable.
I am using the example found here: Read each line in a txt file and assign variables using windows dos commands, which is:
SETLOCAL EnableDelayedExpansion
SET count=1
FOR /F "tokens=* delims= usebackq" %%x IN ("%TEXT_T%") DO (
SET var!count!=%%x
SET /a count=!count!+1
)
ENDLOCAL
Now, all I want to do is echo the results of each variable, but I just can't seem to be able to do so. Below is what I have, which gives me the results for var0 three times--my text file has three lines. I changed the start of count from the above code from 1 to 0.
#echo off
SETLOCAL EnableDelayedExpansion
SET count=0
FOR /F "tokens=* delims= usebackq" %%x IN ("afile.txt") DO (
SET var!count!=%%x
echo !var%count%!
SET /a count=!count!+1
echo !count!
)
ENDLOCAL
While this code
echo !var%count%!
seems logic, as count is changed inside the loop, you can not access the value of the variable without delayed expansion. But
echo !var!count!!
^...^ ^^
first variable second "variable"
will not work, as the parser is not able to properly determine where each variable reference start/end
You can use any of the two following lines
for %%a in (!count!) do echo !var%%a!
call echo %%var!count!%%
How does it work?
The original idea is good, but there are limits in the syntax. We need delayed expansion over the var.. variable, but also over the count.
1.- In the first solution, we store the delayed expansion of the count variable inside the for replaceable parameter, that is used instead of the count variable in the echo command, keeping the delayed expansion only around the var variable.
!var!count!! => %%a=!count! => !var%%a!
2.- In the second solution, a call command is used. This call causes a double evaluation of the line, first on the line parse to execute the call, second while call execution. That is, the line
call echo %%var!count!%%
is first parsed, and the only variable referenced in it is !count! (a double percent sign is a single escaped percent sign), so the line is translated into (suppose count contains 5)
call echo %var5%
Now, the call is executed, line is parsed again to execute the echo and the value of the correct variable is retrieved.
I am fighting with little piece of code for last two days.
In this I am not able to set variable in a for loop.
I want to assign a filename to a variable for string manipulation.
echo off
for /f %%a IN ('dir /b *_ah.ttf') DO (
set /a fName=%%~na
echo %fName%
)
When I echo fName variable I get only last filename repeatedly number of times for for loop count.
(I want to pass this variable as an argument to some batch file as follows
ttfhnt --strong-stem-width=D -i %%a %fName:~0,-3%.ttf
but its failing due to above problem)
Can somebody help me please?
When the cmd parser reads a line or a block of lines (the code inside the parenthesis), all variable reads are replaced with the value inside the variable before starting to execute the code. If the execution of the code in the block changes the value of the variable, this value can not be seen from inside the same block, as the read operation on the variable does not exist, as it was replaced with the value in the variable.
This same behaviour is seen in lines where several commands are concatenated with &. The line is fully parsed and then executed. If the first commands change the value of a variable, the later commands can not use this changed value because the read operation replace.
To solve it, you need to enable delayed expansion, and, where needed, change the syntax from %var% to !var!, indicating to the parser that the read operation needs to be delayed until the execution of the command.
And set /A is only used for arithmetic operations
setlocal enabledelayedexpansion
for /f "delims=" %%a IN ('dir /b *_ah.ttf') DO (
set "fName=%%~na"
echo "!fName!" "!fName:~0,-3!"
)
edited to adapt to comments
While for command is able to execute a command (in the OP code, the dir...), retrieve its output and then iterate over the lines in this output, the original reason for the command is to iterate over a set of files. In this form, the code can be written as
setlocal enabledelayedexpansion
for %%a IN ("*_ah.ttf") DO (
set "fName=%%~na"
echo "!fName!" "!fName:~0,-3!"
)
Now, the for command replaceable parameter will iterate over the indicated set of files. (execute for /? for a list of all the command options).
But as foxidrive points, the problem with delayed expansion are the exclamation signs. Without delayed expansion, they are another normal character, but with delayed expansion they frequently become a problem when a value containig them is assigned/echoed.
A quick test
#echo off
setlocal enabledelayedexpansion
set "test=this is a test^!"
echo ---------------------
set test
echo ---------------------
echo delayed : !test!
echo normal : %test%
for /f "delims=" %%a in ("!test!") do echo for : %%a
Will show
---------------------
test=this is a test!
---------------------
delayed : this is a test!
normal : this is a test
for : this is a test
Obviously when the value is a file name, this behaviour will make the code find or not the file.
Depending on the case different solutions can be used, but usually it involves the activation / desactivation of the delayed expansion behaviour (beware, the endlocal removes any change in environment variables from the previous setlocal).
#echo off
setlocal enabledelayedexpansion
set "test=this is a test^!"
echo ---------------------
set test
echo ---------------------
echo delayed : !test!
rem Commuted to no delayed expansion
setlocal disabledelayedexpansion
echo normal : %test%
endlocal
rem Cancelled the initial enable delayed expansion
for /f "delims=" %%a in ("!test!") do endlocal & echo for : %%a
rem The last endlocal has removed the changes to the variable
echo no data : [%test%]