Increment a variable already incremented in another loop - batch-file

I would like to automate the execution of several commands to retrieve files from pods, with oc client.
I succeeded in incrementing the name of my variables, but I can't increment them in another loop to get their content.
#echo off
set /p "nbPod=How many times should RSYNC be used?? "
set num=0
for /l %%p in (1, 1, %nbPod%) do (
set /a "num = num + 1"
setlocal EnableDelayedExpansion
set /p "podName!num!= Enter pod name for pod number %%p ? "
)
set /a iteration=1
:loop
if %iteration% leq %num% (
setlocal EnableDelayedExpansion
echo %podName!iteration!%
::oc rsync %podName!iteration!%:/my_command_0
::oc rsync %podName!iteration!%:/my_command_1 [etc]
set /a "iteration = iteration + 1"
goto :loop
)
NB : I don't know why but set /a num+=1 wasn't working.

#ECHO OFF
SETLOCAL EnableDelayedExpansion
:: remove variables starting podname
FOR /F "delims==" %%b In ('set podname 2^>Nul') DO SET "%%b="
set /p "nbPod=How many times should RSYNC be used?? "
for /l %%p in (1, 1, %nbPod%) do set /p "podName%%p= Enter pod name for pod number %%p ? "
for /l %%p in (1, 1, %nbPod%) do (
echo !podName%%p!
rem within a block, use REM, not :: as :: is a broken label which terminates the block
rem oc rsync !podName%%p!:/my_command_0
rem oc rsync !podName%%p!:/my_command_1 [etc]
)
GOTO :EOF
Always verify against a test directory before applying to real data.
A few little issues with your code.
setlocal makes a copy of the current environment and processing continues using that new environment. An endlocal statement disposes of the new environment and restores the original. Reaching physical end-of-file is an implicit endlocal.
Consequently, it's normal to follow the initial #echo off with a setlocal as any statements executed before the setlocal are retained in the environment after the batch ends, which can cause confusion for subsequent batches as the variables use may be unexpectedly set.
There is a limit to the number of nested open setlocals that can be used (about 300). Unlikely to be a problem in this case, but something to note.
nbpod is your limit, so there's no requirement to use num or iteration.
I've no idea what the comments in the final loop are, but they MUST be rem statements within a block (parenthesised sequence of commands). OK- probably they are commented-out what I'm actually doing lines....
The for /f ... %%b command will process the list generated by set command, which would be something like
podname1=one
podname2=two
The delims= option causes for/f to assign the (default) first "token" in the line (podname1,podname2) to %%b and "sets" the value to nothing, deleting any stray variables from the environment. The 2^>nul suppresses error messages if there are no current variables named podname....
Then input the names as podname1... according to %%p.
And read them back using delayedexpansion, the current value of [podname strung with %%p]

Related

Avoid a null value in last for loop iteration?

I have a text file with one string per line (only a couple lines total). This file needs to be searched and each string stored. The script will ultimately prompt the user to choose one of the stored strings (if more than one string/line is present in the file), but the for loop is iterating an extra time when I don't want it to.
I'm using a counter to check the number of iterations, and I also read that it's probably a NL CR regex issue, but the finstr /v /r /c:"^$" in the for file-set like in this post Batch file for loop appears to be running one extra time/iteration doesn't work for me (but I probably don't understand it correctly).
The "pref" term is because the strings are to be eventually used as a prefix of files in the same folder...
#echo off
setlocal enabledelayedexpansion
set /a x=1
for /f %%a in (sys.txt) do (
set pref!x!=%%a && echo %^%pref!X!%^% && set /a x+=1
)
echo last value of x = !x!
for /L %%a in (1,1,!x!) do (
echo !pref%%a!
)
REM The rest would be to prompt user to choose one (if multiple) and
REM then use choice as a prefix with a ren %%a %prefX%%%a
If the "sys.txt" contains three lines with strings A, B, C respectively, then the output I currently get is:
pref1
pref2
pref3
last value of x = 4
A
B
C
ECHO is off.
ECHO is off. is not desired, clearly.
You just need to change your increment structure like this. (set it before each line starting from a base of 0)
#Echo Off
Setlocal EnableDelayedExpansion
Set "i=0"
For /F "UseBackQ Delims=" %%A In ("sys.txt") Do (
Set/A "i+=1"
Set "pref!i!=%%A"
Echo(pref!i!)
Echo(last value of i = %i%
For /L %%A in (1,1,%i%) Do Echo(!pref%%A!

Reading a csv file with batch to create users

I'm an IT student. I was trying to make users in my virtual machine of windows server 2008, i though that should be a better way to make them less than make it one by one, then i found that i can make users with a database, in that case csv, and a loop script. But cause my low level of knowledge about commands and batch files i can't achieve my goal. This is what i have in the csv file:
Pedro,12345,939293c
Juanjo,23456,213123v
Eufrasio,34567,2131312b
Dani,45678,123213n
Pepe,56789,12344v
Manolo,67891,12312567b
And this is the batch I've made, I'm stuck in the part of the echo, the command didn't read
SET LOCAL
#echo incio del script
set n1=
set n2=
set n3=
for /F "tokens=1,2,3* delims=," %%i in (Libro2.csv) do (
set "n1=%%i"
set "n2=%%j"
set "n3=%%k"
echo %n1 %n2 %n3
)
pause
Variables in batch are referenced with %var%, not %var.
But in blocks (between ( and ) you need to enable delayed expansion to show the current value (if you change them inside the block):
setlocal enabledelayedexpansion
set x=one
if 1==1 (
set x=two
echo %x% !x!
)
change your script a little bit:
SETLOCAL enabledelayedexpansion
#echo incio del script
set n1=
set n2=
set n3=
for /F "tokens=1,2,3 delims=," %%i in (Libro2.csv) do (
set "n1=%%i"
set "n2=%%j"
set "n3=%%k"
echo %%i %%j %%k
echo !n1! !%n2! !%n3!
)
(Note: you can directly work with the for-variables - see the first echo in the block)
(Note: tokens=1,2,3,* means: for the fourth token take the rest of the line. You don't really need it here, as you don't use %%l)

Setting up variable in for loop in batch

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%]

Trying to reformat a very large csv with a batch file

I have an application that exports data in the format:
1a,1b,1c1,1c2,1c3, ... (up to 1c100),1d1,1d2,1d3, ... (up to 1d100)
2a,2b,2c1,2c2,2c3, ... (up to 2c100),2d1,2d2,2d3, ... (up to 2d100)
etc.
and I am trying to reformat this into
1a,1b,1c1,1d1
1a,1b,1c2,1d2
.
.
1a,1b,1c100,1d100
2a,2b,2c1,2d1
2a,2b,2c2,2d2
etc.
I figured that if this can be done a row at a time I can just loop through the file. However I can't find a way of doing a single row with either tokens, a list, or even as a string function. There is too much data to process in a single operation (each value is about 12 chars). Tokens limit at (roughly) 64/202, a list at about 107/202 and a string at about 1000/2300
Does anyone know how this can be written into a new file?
I was trying things like:
#echo off
setlocal enableDelayedExpansion
set dimCnt=0
<example.csv (
set /p "dimList=" >nul
for %%D in (!dimList!) do (
set /a dimCnt+=1
set "dim[!dimCnt!]=%%D"
)
)
echo
for /l %%I in (3 1 102) do echo !dim[1]!,!dim[2]!,!dim[%%I]!
</code>
..besides the fact that I have missed out the last variable in the line (need to add 100 to it), I can't get more than about 80-110 values out of the list (I guess it depends on value string length)
#echo off
setlocal enableextensions enabledelayedexpansion
(for /f "tokens=1,2,* delims=," %%a in (example.csv) do (
set "data=%%c"
set "i=0"
for %%f in ("!data:,=" "!") do (
set /a "i+=1"
set "d[!i!]=%%~f"
)
set /a "end=!i!/2"
set /a "j=!end!+1"
for /l %%i in (1 1 !end!) do (
for %%j in (!j!) do echo %%a,%%b,!d[%%i]!,!d[%%j]!
set /a "j+=1"
)
)) > output.csv
endlocal
This iterates over the file, getting the first two tokens in the line (%%a and %%b), the rest of the line (%%c) is splitted and each value stored in an environment variable array (kind of). Then, the array is iterated from the start and from the middle, reading the needed values to append to %%a and %%b and generating output file.
#ECHO OFF
SETLOCAL
(
FOR /f "tokens=1,2,*delims=," %%a IN (u:\long.csv) DO (
SET rpta=%%a
SET rptb=%%b
CALL :rptcd %%c
)
)>newfile.txt
GOTO :EOF
:rptcd
SET /a lines=100
SET lined=%*
FOR /l %%x IN (1,1,99) DO CALL SET lined=%%lined:*,=%%
:loop
IF %lines%==0 GOTO :EOF
SET /a lines-=1
CALL SET lined=%lined:*,=%
FOR /f "delims=," %%x IN ("%lined%") DO ECHO %rpta%,%rptb%,%1,%%x&shift&GOTO loop
GOTO :eof
This should get you going - just need to change the input filename and output filename...
Your code does not work because SET /P cannot read more than 1023 bytes. At that point it returns the data read so far, and the next SET /P picks up where it left off. Adapting your code to compensate will be very difficult. You would be better off using FOR /F as in MC ND's answer. But beware, batch has a hard limit of 8191 characters per line in pretty much all contexts.
Better yet, you could use another scripting language like JScript, VBS, or PowerShell. Performance will be much better, and the code much more robust and far less arcane. I love working with batch, but it simply is not a good text processing language.

CMD/Batch extract each text file line and loop from variable

I have this code
#Echo Off
SetLocal EnableDelayedExpansion
SET mydir=D:\
SET DirCount=2
SET T=
For /F %%i In (qqq.txt) Do (
set fg=%%i
FOR /L %%G IN (2, 1, %DirCount%) DO (call :subroutine "%%i")
)
:subroutine
Set T=!T!../
start /wait %mydir%program.exe %T%%fg%
echo %t%%fg% >>%cd%see.log
qqq.txt has rows
1
2
3
In result i get:
../1
../../2
../../../3
../../../../3
But I need like this:
../1
../../1
../2
../../2
../3
../../3
Please Help what I'm doing wrong?
Just a little additional requried
I need what first ocur in
set "T=!T!../"
will be without dots
set "T=!T!/"
result must be
/1
../1
../../1
/2
../2
../../2
how and where to add counter and make visible for each loop somthing like
echo Now looping: %%i row of %countrow(in qqq.txt), left %countrow-%%I
echo Now looping: Dir Nr%%G !T!%%i
Sorry for stupid questions but batch coding is difficult to me.
You have 3 problems:
1) You don't exit your program after the main loop so it falls through into the subroutine
2) Your FOR /L IN() clause is wrong, the first number should be 1, not 2
3) You need to reset T back to undefined within the outer loop.
EDIT
I believe you may have an additional problem. When redirecting output you prefix your file name with %cd%. I don't think that is giving the result you want. If your current directory is C:\test, then >>%cd%see.log will result in output sent to file named testsee.log in the root C:\ directory. If you wanted the output in the current directory then it should be >>%cd%\see.log, but the path info is not really needed since the default is to use the current directory. So really all you would need is >>see.log. Since I don't know your intent, I have left the redirection as originally written. End Edit
#echo Off
SetLocal EnableDelayedExpansion
SET mydir=D:\
SET DirCount=2
For /F %%i In (qqq.txt) Do (
SET T=
set fg=%%i
FOR /L %%G IN (1, 1, %DirCount%) DO (call :subroutine "%%i")
)
exit /b
:subroutine
Set T=!T!../
start /wait %mydir%program.exe %T%%fg%
echo %t%%fg% >>%cd%see.log
There is no need to call a subroutine in your case, especially since you have already enabled delayed expansion. The following gives the same result with less code, and it is faster because it avoids the relatively slow CALL statement. (performance is probably not an issue in this small example, but if using loops with many iterations it can make a huge difference)
#echo Off
SetLocal EnableDelayedExpansion
SET mydir=D:\
SET DirCount=2
For /F %%i In (qqq.txt) Do (
SET "T="
FOR /L %%G IN (1, 1, %DirCount%) DO (
set "T=!T!../"
start /wait %mydir%program.exe !T!%%i
echo !T!%%i >>%cd%see.log
)
)
Partial answer to additional questions
You can use FOR /F to capture the result of FIND /C to get the number of lines in "qqq.txt". This should be done before you enter the main loop:
for /f %%N in ('find /c /v "" ^<qqq.txt') do set "rowCount=%%N"
You can use SET /A to do basic math, thus enabling you to keep track of the number of remaining lines
First initialize the remaining count before entering the main loop:
set "remainingLines=%rowCount%"
Then within the main loop use SET /A to decrement the value.
set /a remainingLines-=1

Resources