batch file fails to set variable in IF clause - variables

The following code is not updating Run to equal N even though the match occurs. this means I'm not dropping into the CALL code. Am i missing something here?
SET Run=Y
REM Check current files date/time information and establish if the file has been present too long in the directory
REM Skip first 4 lines as header information not required
FOR /f "tokens=1-5 skip=4 delims= " %%G IN ('dir /OD "%SRCH_CRITERIA% "') DO (
ECHO "Params to processFile: " %%G %%H %%I ""%%K""
IF %%K.==. (
ECHO "K:nothing"
SET Run=N
ECHO %Run%
)
IF %%K==free (
ECHO "K:FREE"
SET Run=N
ECHO %Run%
)
ECHO %Run% RUN
IF %Run%=="Y" (
CALL :processFile "%%G" "%%H" "%%I" "%%K"
)
)

You need to use the delayed expansion option of cmd.exe.
At the top of your script, put:
setlocal enableextensions enabledelayedexpansion
and then put:
endlocal
at the bottom.
Then you need to use !Run! instead of %Run%.
The reason your code is not working is that the entire FOR statement (including the commands within it) is evaluated when it's encountered. That's the point where the %Run% variables are expanded.
By using deferred expansion, you don't expand them until they're actually needed (after you've set them within the block).
You can see the difference in this script:
#echo off
setlocal enableextensions enabledelayedexpansion
set xx=0
for %%i in (a b c d) do (
echo %%i
set /a "xx = xx + 1"
if %xx%==3 echo yes for normal
if !xx!==3 echo yes for delayed
)
endlocal
which outputs:
a
b
c
yes for delayed
d
You'll notice that the check with %xx% does not work because that was evaluated when the for statement started (and xx was 0). The delayed-expansion !xx! does work since that is evaluated each time through the loop.

Related

Batch: variable indirection: getting the value of a variable by dynamically constructed name

So when I run this code:
#echo off
setlocal enabledelayedexpansion
set lstFolders= First Second
set intCounter=0
for %%i in (!lstFolders!) do (
set /a intCounter += 1
set strFlder=%%i
set strFolder!intCounter!=!strFlder!
echo %%i
echo !strFlder!
echo !strFolder%intCounter%!
echo !strFolder1!
echo !strFolder2!
)
:End
pause
endlocal
It results with this:
First
First
ECHO is off.
First
ECHO is off.
Second
Second
ECHO is off.
First
Second
Why doesn't it allow me to echo the variable create with the format : !strFolder%intCounter%!? Is there another way to reference this variable and get the data that is inside of it?
As #mklement0 already said, you need an extra evaluation step.
But inspite to use echo "%%strFolder!intCounter!%%", I would recommend delayed expansion.
As delayed expansion is immune to any content, echo "%%strFolder... will fail with content containing quotes or exclamation marks.
#echo off
setlocal enabledelayedexpansion
set "lstFolders=First Second"
set intCounter=0
for %%i in (!lstFolders!) do (
set /a intCounter+= 1
set "strFlder=%%i"
set "strFolder!intCounter!=!strFlder!"
echo !strFlder!
set "varname=strFolder!intCounter!"
for /F "delims=" %%A in (""!varname!"") do echo Indirect %%~A=!%%~A!
)
The doubling of the quotes avoids problems with the eol character of the FOR/F loop.
Caveat: The code below only works with list values (the tokens of %lstFolders% such as First) that:
contain neither spaces
nor any of the following chars.: & | < > "
A different looping approach would be needed to handle such cases.
#echo off
setlocal enabledelayedexpansion
set "lstFolders=First Second"
set intCounter=0
for %%i in (%lstFolders%) do (
rem Increment the counter
set /a intCounter += 1
rem Echo the loop variable
echo #!intCounter!=%%i
rem Set variable strFolder<intCounter> to the loop variable's value
set "strFolder!intCounter!=%%i"
rem Echo the variable created using variable indirection with for /f ('...')
for /f "delims=" %%v in ('echo "%%strFolder!intCounter!%%"') do set "thisFolder=%%~v"
echo %%thisFolder%% ^(via %%strFolder!intCounter!%%^)=!thisFolder!
)
Running the above yields:
#1=First
%thisFolder% (via %strFolder1%)=First
#2=Second
%thisFolder% (via %strFolder2%)=Second
What you're looking for is variable indirection:
While you can set a variable indirectly (by a name that you construct dynamically from the value of another variable), e.g.,
set "strFolder!intCounter!=%%i", with !intCounter! having a value of 1, correctly sets variable strFolder1 to the value of %%i),
you cannot get a variable's value that way; you need an extra evaluation step, which for /f ... ('echo ...') can provide.:
for /f "delims=" %%v in ('echo "%%strFolder!intCounter!%%"') do ... parses the output of the command in single quotes (echo ...) and assigns the result as a whole (delims=) to variable %%v
(%%~v removes the enclosing double quotes, which were added around the echo argument to make the command handle shell metacharacters such as & | < > correctly).
%%strFolder!intCounter!%% immediately evaluates strFolder!intCounter! to strFolder1, if !intCounter! is 1, which, thanks to the enclosing doubled % instances, ends up as literal %strFolder1%, which is what the echo command sees when it is run by the for command, causing it to evaluate the variable reference and expand to its value.

Batch failure set value to a variable

this is my code:
#echo off
Setlocal EnableDelayedExpansion
set log=C:\mylog.log
set ftpFolder=C:\contributor\ftp
set rotterdamFolder=C:\rotterdam
cd /D C:\contributor
echo [FTP Folder: %ftpFolder%] >> %log%
cd /D %ftpFolder%
for /D %%f in (*) do (
cd %%f
for %%i in (*) do (
echo [FTP, %%f] Sending %%i >> %log%
for /f "tokens=1,2" %%a in (C:\input.txt) do (
if %%a==%%f (
set et=%%b
)
)
copy %ftpFolder%\%%f\%%i %rotterdamFolder%\%et% >> %log%
)
cd .. >> %log%
)
input.txt file is something like:
007 87855
008 87823
015 87830
it's not important how the two columns are divided (tab or space).
my problem is that %et% variable is not defined. how is it possible? I need to use variable "b" as destination folder. where is the error?
%et% is expanded when the line is parsed, and the entire outer parenthesized FOR loop is parsed all at once. So the value you get is the value that existed before your loop was entered.
You could solve the problem by enabling delayed expansion and using !et! instead. Type HELP FOR or FOR /? from the command line for more information. The section about delayed expansion is about halfway down.
But there really isn't any need to use the et variable at all. I believe you only want to copy the file when the first column of the inner loop matches the folder name. Simply move and substitute the COPY command for the SET statement. Use %%b instead of %et% in the COPY statement.
It wasn't necessary, but I substituted PUSHD/POPD for the CD commands. The redirection with CD is pointless when a path is specified.
You really should quote your paths in case they contain spaces or special characters.
for /D %%f in (*) do (
pushd "%%f"
for %%i in (*) do (
echo [FTP, %%f] Sending %%i >> %log%
for /f "tokens=1,2" %%a in (C:\input.txt) do if "%%a"=="%%f" (
copy "%ftpFolder%\%%f\%%i" "%rotterdamFolder%\%%b" >> %log%
)
)
popd
)

Importing variables from Text file, variables not incrementing

My goal is to be able to go into a specific folder, run a task, then when that task is completed, move to another folder and repeat.
#echo off
setlocal ENABLEDELAYEDEXPANSION
set vidx=0
for /F "tokens=*" %%A in (z:\desktop\cookieclean\test.txt) do (
SET /A vidx=!vidx! + 1
set var!vidx!=%%A
)
set var
echo %var!vidx!%
set /a vidx+=1
echo %var!vidx!%
endlocal
That is the code I am working on. I copied the code from somewhere else on the web and tried to get it to do what I wanted. the code above is just me attempting to take the test file, throwing in the 3 lines of text as the variables, then having it increment.
I'm sure it's something super simple, but I've been trying to get this to work for hours, and my rookie-ness is showing.
A pseudo code of what I'm TRYING to do would be
Input variables from text
Open folder containing files
Delete files that are older than X days
Move active folder to next folder in directory
Delete files that are older than X days
Loop
End
I'm just trying to use the cmd line.
You have your delayed and normal expansion reversed. You want the inner variable expanded before the outer one: !var%vidx%!
Your SET /A statement in your loop works, but you do not need to expand the variable. This would work just as well: SET /A vidx=vidx + 1, or better yet: SET /A vidx+=1
I also believe your logic to show results is incorrect. The code below should work.
#echo off
setlocal enableDelayedExpansion
set "vidx=0"
for /f "tokens=*" %%A in (z:\desktop\cookieclean\test.txt) do (
set /a vidx+=1
set "var!vidx!=%%A"
)
set var
set "cnt=vidx"
set "vidx=1"
:loop
if %vidx% leq %cnt% (
echo !var%vidx%!
set /a vidx+=1
goto :loop
)
endlocal
Here is a much simpler and more efficient method using FOR /L instead of a GOTO loop.
#echo off
setlocal enableDelayedExpansion
set "vidx=0"
for /f "tokens=*" %%A in (z:\desktop\cookieclean\test.txt) do (
set /a vidx+=1
set "var!vidx!=%%A"
)
set var
for /l %%N in (1 1 %vidx%) do echo !var%%N!
endlocal

How to add variables in a batch file

I'm trying to get a list of numbers an letters like 11111111 and a letter. Letter si calculated with the number. Something like:
#echo off
set var=23
FOR /L %%H IN (40000000,1,49999999) DO (
set number = %%H+%var%
echo %number%A
)
pause
exit
I want to get (more or less)
400000023A
400000024A
400000025A
...
But without changing the FOR sentence... Is that possible?
To continue with what jeb stated, for your requested output, the code should be this:
#echo off & setlocal EnableDelayedExpansion
set var=23
FOR /L %%H IN (40000000,1,49999999) DO (
set /a number=%%H+!var!
echo !number!A
)
endlocal
pause
exit
Avoid spaces with set statements
Use set /a if you want to calculate
set /a number=%%H+var
Avoid percent expansion in a block (and also in a FOR block)
Use delayed expansion instead (add a setlocal EnableDelayedExpansion to your file)
echo !number!A
To use numbers in batch use
set /a
See set /? for all the info.
jeb provided the hints needed to fix your code, and Jeff K actually implemented the fix.
However, since you are adding a constant to every value, it is simpler and more efficient to add the constant to the FOR /L start and end values.
#echo off
setlocal
set var=23
set /a "start=40000000+var, end=49999999+var"
for /l %%H in (%start%, 1, %end%) do echo %%HA
There is, but with changing the FOR sentence.
You actually forgot the setlocal command right there.
setlocal EnableDelayedExpansion
This command is needed in order to add variables.
Still don't get it?
Here's the code so that you will add variables.
#echo off
setlocal EnableDelayedExpansion
cls
set var=17
echo %var%
pause
set /a var=%var%+7
echo %var%
pause
Try to find any errors and mistakes on my code. Then test it.
There you have it! Added variables!
The best method is:
set /a counter+=1
so:
#echo off
set /a counter=
:Top
set /a counter+=1
echo 4000000%counter%A
GOTO Top
You can copy the code above.

concatenate multiple variable's names in batch/CMD

searched this site and others - no joy.
:: first for loop
for /L %%x in (1,1,2) do (
generic-executable.output > grab1-%%x.txt
:: second, nested for loop
for /f "delims=" %%i in (grab1-%%x.txt) do (set grab1=%%i)
echo variable string is %grab1%%x%
generic-executable.output > grab2-%%x.txt
for /f "delims=" %%i in (grab2-%%x.txt) do (set grab2=%%i)
echo variable string is %grab2%%x%
)
Trying to run a nested for loop that will
1) write data to the file
2) take data from the file and save it to another variable.
The names of the end variables should be a concatenation of each of the for loops (i.e. grab1-1, 2-1, 1-2, 2-2).
Saving the data to the variables is no problem, formatting the variables to recall the data IS.
I'm most likely missing something in the formatting of the concatenated variable. I've tried single ', double ", ^, !, one %, two %, backslash, ACK!! ... the closest I've gotten is
echo %grab1-%%x
gave:
%grab1-1
I'd appreciate any tips you can provide.
Thanks,
Dave
You have run into a classic stumbling block for batch newbies: You cannot set a variable within a loop (within parentheses) and then access the value using %var% within the same loop. The Entire loop (parenthesized block of code) is parsed in one pass, and %var% is expanded at parse time. So you see the value of var as it was prior to the loop executing.
The solution is to enable delayed expansion using setlocal enableDelayedExpansion near the top of your script, and then expand the variable using delayed expansion as !var!. Delayed expansion means the value is expanded at run time - exactly what you want.
I believe this is what you were trying to achieve
setlocal enableDelayedExpansion
for /L %%x in (1,1,2) do (
genericOutput1.exe > grab1-%%x.txt
for /f "delims=" %%i in (grab1-%%x.txt) do set "grab1-%%x=%%i"
echo grab1-%%x variable string is !grab1-%%x!
genericOutput2.exe > grab2-%%x.txt
for /f "delims=" %%i in (grab2-%%x.txt) do set "grab2-%%x=%%i"
echo grab2-%%x variable string is !grab2-%%x!
)
::List all of the grab variable defined
set grab
You don't need to save the output of your executables to a file. (Unless of course that is your requirement). You can use FOR /F to process the output directly. The FOR command has many variants that look nearly identical, yet behave very differently. This variant uses single quotes to cause FOR /F to process a command.
setlocal enableDelayedExpansion
for /L %%x in (1,1,2) do (
for /f "delims=" %%i in ('genericOutput1.exe') do set "grab1-%%x=%%i"
echo grab1-%%x variable string is !grab1-%%x!
for /f "delims=" %%i in ('genericOutput1.exe') do set "grab2-%%x=%%i"
echo grab2-%%x variable string is !grab2-%%x!
)
::List all of the grab variable defined
set grab
Note that FOR /F will iterate each line (whether it be from a text file or from command output). Your algorithm will only save and print the content of the last line - each successive line will overwrite the value from the prior line.