Modifying variable values inside a For loop in Windows shell script - batch-file

I need to perform operations on first 4 files in a folder. So, the counter variable has to increment during each operation of the For loop. The following piece of code:
#ECHO OFF
SETLOCAL
SET TESTDIR1=D:\Local Data\Shell Script\test folder 1
SET _transfer=XXXX
FOR /f "delims=" %%i IN ('dir "%TESTDIR1%\*" /b/a-d/o-d') DO (
SET _transfer=%_transfer:~1%
ECHO %_transfer%
)
prints "XXXX" for every iteration of the loop. Why might that be?

Try
FOR /f "TOKENS=1*delims=[]" %%i IN ('dir "%TESTDIR1%\*" /b/a-d/o-d^|find /n /v ""') DO (
if %%1 LEQ 4 echo %%j
)
Your approach doesn't work because batch replaces any %var% within a block (a parenthesised sequence of commands) with the value of var before the block is executed. See any number of SO questions related to delayed expansion for more info.

Related

Nested for loop - batch Script

Hello Batch File experts,
I wrote this piece of code which will print the Latest file version present in the folder in comparison to file name sent as argument, however these line seems to work accordingly when I remove the outer for loop, which I designed to loop as many time as CLI arguments.
FOR /f %%f IN ('DIR /b %%a.*.zip') DO #SET last=%%f
ECHO %last%
Full code :
cd C:\Users\batch\Desktop\test
chdir
set arg1=%1
set arg2=%2
set list=%arg1% %arg2%
(for %%a in (%list%) do (
FOR /f %%f IN ('DIR /b %%a.*.zip') DO #SET last=%%f
ECHO %last%
))
pause
what am I missing here because of which variable last is not set with value when outer loop is present which works perfectly without it.
Thanks,
Might I suggest you use SHIFT instead:
#Echo Off
SetLocal
If %1'==' Exit/B
If /I Not "%CD%"=="%USERPROFILE%\Desktop\test" (
PushD "%USERPROFILE%\Desktop\test" 2>Nul&&(Set _=PopD)||Exit/B)
:Loop
For %%A In ("%~1*.zip") Do Set "last=%%A"
Echo=%last%
Shift
If Not %1'==' GoTo Loop
%_%
EndLocal
Timeout -1
This of course means that you are free to use it with more than two arguments!
You need to use delayed expansion (about ten thousand SO items on this) or use a subroutine or
call echo %%last%%

Batch scripting not assigning variable as expected

I'm working on a batch script which will assign a variable string and then trim it. I'm facing two issues:
The variables are not assigning properly. It is taking last value from from the variable file.
The variables are not assigning the first time I run the script. I need to run the script second time to see if the variables have been assigned. On third run, I can see the trim is working.
My script looks like this:
#echo off
for /f "tokens=*" %%a in ('findstr "W3SVCWINSRVRHOSTS" "C:\Data\SiebelAdmin\Commands\file.txt"') do (
for /f "tokens=2 delims==" %%b in ("%%a") do (
for %%c in (%%b) do (
echo in loop
set str=%%c
echo %%c
echo.%str%
set str=%str:~-6%
echo.%str%
)))
The output looks like this on third run:
> C:\Users\parthod\Desktop>b.bat
in loop
xsj-uvapoms72
7.2.27
7.2.27
in loop
xsj-uvapoms82
7.2.27
7.2.27
in loop
172.17.2.26
7.2.27
7.2.27
in loop
172.17.2.27
7.2.27
7.2.27
You fell into the delayed expansion trap -- try this:
#echo off
setlocal EnableDelayedExpansion
for /f "tokens=*" %%a in ('findstr "W3SVCWINSRVRHOSTS" "C:\Data\SiebelAdmin\Commands\file.txt"') do (
for /f "tokens=2 delims==" %%b in ("%%a") do (
for %%c in (%%b) do (
echo in loop
set str=%%c
echo %%c
echo.!str!
set str=!str:~-6!
echo.!str!
)))
endlocal & set str=%str%
In between the setlocal/endlocal block, delayed variable expansion is active. To actually use this feature enclose the variables by !! rather than %%.
Since setlocal sets up a new namespace for variables, the compound endlocal & set statement is required to transfer the value of str beyond the block.

Using if condition inside a for loop using batch script

I have a script which need to be iterating through the file and checking a condition, when it meets condition every time corresponding values should be stored to a variable and displayed.
for /f "tokens=1,6,8 delims= " %%D in (test.txt) do (if %%D==01 (set x=%%I
set y=%%K)
echo %x% %y%)
Can some one help me on the above code?
The tokens are asigned to the nominated control (%%D) and then %%E, %%F.
Solution: replace %%I with %%E and %%K with %%F
BUT within a block statement ( a parenthesised series of statements) %var% refers to the value of var at the time the statement is parsed, that is, checked for validity, not at run-time, that is, as the value changes through the operation of the loop.
To see the value as it changes, the easiest method is to change the echo statement to
call echo %%x%% %%y%%
For more information, see any of hundreds of SO responses on the subject of delayed expansion.
It could be done as below.
#echo off
setlocal enableextensions enabledelayedexpansion
for %%F in ("C:\Users\JOhn\Desktop\Check\*.txt") do SET input=%%F
for /f "tokens=1-10 delims= " %%a in (%input%) do (
if %%a==01 (
set "_y=%%f"
set "_z=%%h")
echo %%a %%b %%c %%d %%e %%f %%g %%h %%i %%j !_y! !_z!)>>temp.txt

Numbering text files in a folder

I have some text files placed in the same folder as a batch file. I need the batch file to echo the filenames without its extention line by line with a numbering. It has to be in alphabetical order. Also, it needs to store the file name into a variable fruitx where x is the number of its numbering. For example...
If I have three files in the folder:
Banana.txt
Cherry.txt
Apple.txt
I need the batch file to echo:
1) Apple
2) Banana
3) Cherry
Then, I need the variable %fruit1% be "Apple", %fruit2% be "Banana" and %fruit3% be "Cherry".
set x=0
for /F "tokens=* delims=" %%I in ('dir /b *.txt') do (
set /a x=x+1&echo %x%^) %%~nI&set fruit%x%=%%~nI)
echo %x%
Here is my code. It doesn't work and I can't figure out why. It echos 0 for each of the numberings instead.
Sorry if this sounds confusing. Thanks in advance!
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.
In your case, the problematic variable is x, that changes its value inside the loop and needs this value inside the same loop
#echo off
setlocal enableextensions enabledelayedexpansion
set "x=0"
for /f "delims=" %%a in ('dir /a-d /b /on *.txt 2^>nul') do (
set /a "x+=1"
echo !x!^) %%~na
set "fruit!x!=%%~na"
)
echo(---------------------------
set fruit
Also, an alternative approach without delayed expansion can be to filter the generated list of files with findstr to generate the number for each element
#echo off
setlocal enableextensions disabledelayedexpansion
for /f "tokens=1,2 delims=:" %%a in ('dir /a-d /b /on *.txt 2^>nul ^| findstr /n "^"') do (
echo %%a^) %%~nb
set "fruit%%a=%%~nb"
)
echo(---------------------------
set fruit
#echo off
setlocal enableDelayedExpansion
set counter=0
for /f %%a in ('dir /b /a:-d /o:n *.txt') do (
set /a counter=counter+1
echo !counter! ^) %%~na
set "fruit!counter!=%%~na"
)
echo listing fruits
set fruit

batch script to delete every other file in directories

I have a directory with many sub-directories that contain thousands of jpgs. What I want to do is create a batch script that will go through all of the sub-directories and delete every 2nd image i.e. keep the first, third, fifth image but delete the second, fourth, and six image etc per directory (ordered by filename).
I tried with the following but my knowledge of batch scripting is poor, and the syntax is clearly incorrect.
#echo off
set z = 0
for /f %%a in ('dir/b *.jpg')
do (
set z = z + 1
if z == 2 del %%a
)
The DO must be on the same line as FOR.
You must use SET /A if you want to do math
Your logic is wrong - Currently it will only delete the 2nd file, not every other one. You should take the mod 2 value (remainder devided by 2) and delete if the result is 0.
You must use %z% if you want to see the current value (except within a SET /A statement). But that will not work inside a code block that just set the value. In that case you need to enable delayed expansion and use !z! instead.
Expanding a FOR variable that contains ! (valid in file names) while delayed expansion is enabled will corrupt the value. So delayed expansion must be toggled on and off
You say you want to recurse sub-directories, but your code only looks at one folder.
Spaces are significant in the SET statement. Your code defines a variable z with a space at the end of the name. Not what you want.
Here is a debugged version:
#echo off
setlocal
for /r %%D in (.) do (
set "z=0"
for /f %%F in ('dir /b "%%D\*.jpg"') do (
set /a "z+=1, r=z%%2"
setlocal enableDelayedExpansion
if !r! equ 0 del "%%D\%%F"
endlocal
)
)
There are ways to solve this without delayed expansion. One is to simply alternate between defining and undefining a variable.
#echo off
setlocal
for /r %%D in (.) do (
set "del="
for /f %%F in ('dir /b "%%D\*.jpg"') do if defined del (
del "%%D\%%F"
set "del="
) else set "del=1"
)
Another is to intentionally divide by 0 when you want to delete, and delete only when there is an error. Error messages are hidden by 2>nul, and the || operator conditionally executes the following command only if the prior command failed.
#echo off
setlocal
for /r %%D in (.) do (
set "z=0"
for /f %%F in ('dir /b "%%D\*.jpg"') do 2>nul set /a "z+=1, 1/(z%%2)" || del "%%D\%%F"
)
try this and remove the echo if the output looks good:
#echo off &setlocal
for /f "tokens=1*delims=:" %%a in ('dir /b /s /a-d *.jpg^|findstr /n $') do (
echo %%a|findstr "[02468]$" >nul && echo del "%%~b"
)

Resources