I want to perform an operation multiple times from a command window. Common sense tells me that a FOR loop should be able to handle this. Sure enough, if I want to execute, say, myProg.exe, I can open a command window and use:
C:\> FOR %i in (1 2 3) DO myProg.exe
Easy.
But what if I want to execute myProg.exe 1000 times? I want to specify a range in the FOR loop, but I'm having trouble seeing how to do this.
Intuitively, it seems like I should be able to do something like one of the following:
C:\> FOR %i in (1 to 1000) DO myProg.exe
C:\> FOR %i in (1-1000) DO myProg.exe
But, of course, this doesn't work. The FOR loop interprets the list as 3 tokens and 1 token, respectively, so myProg.exe is only executed 3 times and 1 time, respectively.
Batch File Solution
It'd probably be easy to write some sort of batch (.bat) file:
SET COUNT=0
:MyLoop
IF "%COUNT%" == "1000" GOTO EndLoop
myProg.exe
SET /A COUNT+=1
GOTO MyLoop
:EndLoop
But isn't there an easy way to do this from the command line?
You can use the /l tag in your statement to make it loop through a set of numbers.
eg.
C:\> FOR /l %i in (1,1,1000) DO myProg.exe
This says loop through the range, starting at 1, stepping 1 at a time, until 1000
http://ss64.com/nt/for_l.html
for /l %%i in (1,1,100) do echo %%i
add another % sign before i to work
Related
I'm trying to run a batch script through the command line arguments to avoid writing a batch script to file every time I need it.
The sample batch script is
:loop
timeout /t 1
goto loop
I pass it to cmd.exe with the command line
cmd.exe /C ":loop & timeout /t 1 & goto loop"
It works for commands like timeout, but it doesn't work if I include labels such as loop.
It simply exits without saying anything.
Is it possible to do labels and if else statements through the /C command in batch?
You can't use labels on the command line, but if you're looking for an infinite loop, it's possible to abuse the for /L command so that the counter never reaches its target.
for /L %A in (,,) do timeout /t 1
Note that if you want to run this in a script, you need to change %A to %%A because of how escaping %s works.
I'm trying to take some user input defined variables to decide whether or not to enter the loop and execute a copy and rename followed by deletion of the original file as it will no longer be needed.
set /p multiTune="Does your tune file need to be shared with multiple
Element sequences? (y/n) "
Echo Multi Tune is %multiTune%
if "%multiTune%"=="y" (set /p tuneCount="How many sequences will need to share your tune file? ")
Echo Tune count is %tuneCount%
pause
if "%multiTune%"=="y" (SET /p tuneName="Enter the file letter/number combination for the R quant of your tune file. ") else (#ECHO The user specified there is no need for a second tune.)
Echo Tune Name is %tuneName%
pause
if "%multiTune%"=="y" (SET /a tuneCount+=1) else(set /a tuneCount-=tuneCount)
Echo Tune count is now %tuneCount%
pause
:loop
if "%tuneCount%"=="0" goto exitloop
Set /p seqNumber="Enter the number for one of the sequences."
copy %tuneName%.D.pdf "S%seqNumber%-TUN1_%tuneName%.D.pdf"
echo %tuneName%.D.pdf renamed to S%seqNumber%-TUN1_%tuneName%.D.pdf
pause
Set /a tuneCount-=1
if "%tuneCount%"=="1" DEL "%tuneName%.D.pdf"
if "%tuneCount%"==1 goto exitloop
Echo %tuneCount%
pause
goto loop
:exitloop
All of the echos and pauses are just for testing purposes to make sure I have the correct values in my variables.
The batch file runs fine with the variables containing the correct strings and values up until the line:
if "%multiTune%"=="y" (SET /a tuneCount+=1) else(set /a tuneCount-=tuneCount)
The file says something is unexpected and closes at this point so i havent gotten a chance to figure out if the looped portion even works. The point of the +1 is so that it enters the loop and executes the commands until it gets to 1 and skips the loop if it equals 0.
I read a bunch of information about setlocal delayedexpansion and using !'s around variables instead of %'s. I'm not sure how to implement this or if this applies to my problem at all. I know there is probably an easier way to do the if statements but I'm a novice and that was the easiest way for me to understand it as I've been learning on the fly through trial and error, and everything you see is the results of a single day of learning.
Any help would be much appreciated. I tried to be as detailed as possible about what it is I'm trying to do but if you have any questions I will do my best to answer.
I really think you are making the things so MUCH complicated...
Have a corrected piece of the code you provided us (Note: I did not touch the loop subroutine to be on-topic):
#echo off
choice /m "Does your tune file need to be shared with multiple element sequences? (y/n) " /C:yn /N
rem Echo Multi Tune is %errorlevel%
rem If errorlevel equals to 1 user input is "Y", it is 2 it is "N". (I commented the "echo" command as it changes the errorlevel value).
if errorlevel 2 goto question_N
if errorlevel 1 goto question_Y
:question_Y
set /p tuneCount="How many sequences will need to share your tune file? "
set /p tuneName="Enter the file letter/number combination for the R quant of your tune file. "
SET /a "tuneCount+=1"
goto loop
:question_N
set /p tuneCount="How many sequences will need to share your tune file? "
ECHO The user specified there is no need for a second tune.
set /a "tuneCount-=tuneCount"
goto loop
:loop
rem [Code you provided above]
I hope you are fine with this, testing it and it works!
Preface
While writing a separate piece of code, I encountered a problem with question marks in for loops. As shown below, the question mark is not accessed in the for loop.
Batch file:
#echo off
for %%x in (the, quick, ?, brown, fox) do (
echo %%x
)
Output:
the
quick
brown
fox
This also does not work in the CMD (using %x instead of %%x), or when using "", [], ^, \, % or other common methods of character escaping.
Using a counter variable to determine the number of times the code within the parentheses was accessed only results in a total count of 4, meaning it is clearly not a problem with the echo command.
Question
Why doesn't a question mark work in a standard for loop, and how would I go about fixing it?
It's because ? will be expanded into a list of filenames one character long. The "naked" for is using that list as a list of filenames.
If you run the following commands, you'll see this in action:
c:\> echo xx >a
c:\> echo xx >b
c:\> for %i in (1, ?) do echo %x
1
a
b
If you look at Rob van der Woude's excellent scripting pages, you'll see that the for page has options for processing command output, numbers and files - it's not really suited for arbitrary strings.
One way to get around that is to provide your own for-like command as shown in the following example:
#echo off
setlocal enableextensions enabledelayedexpansion
rem Call the callback function for each argument.
set escapee=/
call :doFor :processEach 1 2 ? 4 5
echo.Escapee was %escapee%
rem Execute simple command for each argument.
call :doFor echo 6 7 ? 9 10
endlocal
goto :eof
:processEach
set escapee=%escapee%%1/
goto :eof
:doFor
setlocal
rem Get action.
set cbAction=%1
shift
:dfloop
rem Process each argument with callback or command.
if not "%1" == "" (
call %cbAction% %1
shift
goto :dfloop
)
endlocal&&set escapee=%escapee%
goto :eof
This provides a single functions which can handle both callbacks and simple commands. For more complex commands, provide a callback function and it will get called with each argument in turn. The callback function can be arbitrarily complex but keep in mind that, because it's operating within a setlocal, changes to environment variables cannot escape back to the caller.
As a way around this, it allows one variable, escapee, to escape the scope - you could also add more if needed.
For simple commands (like echo) where you just need the argument placed at the end, you do the same thing. It doesn't need a callback function but it's restricted to very simple scenarios.
Also keep in mind that, although this seems like a lot of code, the vast majority of it only needs to exist in one place. To use it, you simply need a one-liner like the sample:
call :doFor echo my hovercraft is full of eels
Also keep in mind that there may be other characters that do not fare well, even with this scheme. It solves the ? issue but others may still cause problems. I suspect that this would be an ideal opportunity to add PowerShell to your CV, for example, a command that's almost bash-like in it's elegance and zen-ness:
PShell> foreach ($item in #("1", "?", "3", "4")) { echo $item }
1
?
3
4
You could switch to FOR /F.
But FOR /F is used to process multiple lines to split them into tokens.
In your case you don't need multiple tokens, you need one loop per item.
That can be done by splitting the items with linefeeds.
I'm using # as item delimiter, but you are free to use any other character
#echo off
setlocal EnableDelayedExpansion
(set \n=^
%=EMPTY=%
)
set "itemList=the#quick#?#brown#fox"
for %%L in ("!\n!") DO (
FOR /F "delims=" %%x in ("!itemList:#=%%~L!") DO echo - %%x -
)
Output:
- the -
- quick -
- ? -
- brown -
- fox -
I've been coding with batch many years, and I'm suprised to realize this issue until now!
I found another way to deal with this problem. May be somebody prefers it, like me.
In my particularly case, I'm using the FOR LOOP to get some named arguments of the current function. This is what I did:
:SomeFunct
rem Replace ?
set "args=%*"
set "args=%args:?=`%"
rem Iterate args
for %%p in (%args%) do (
for /f "tokens=1,* delims=: " %%a in ("%%~p") do (
rem Get and store values
if /i "%%~a" equ "/a" set "argA=%%~b"
if /i "%%~a" equ "/b" set "argB=%%~b"
if /i "%%~a" equ "/c" set "argC=%%~b"
)
)
rem Restore ?
if defined argA set "argA=%argA:`=?%"
if defined argB set "argB=%argB:`=?%"
if defined argC set "argC=%argC:`=?%"
rem I use the args
rem ...
rem Return
goto:eof
I call the function like this:
rem Calling example
call:SomeFunct "/a:Is there" "/b:a question mark" "/c in the arguments?"
I am trying to use the choice command in a batch script to take the default input if the user doesn't want to stop kicking off a report. I wrote the below script but instead of waiting 10 seconds and kicking off the report, it is recursively echo-ing the first line of the code over and over until I kill the script. Is there something wrong that I am doing?
My Code:
CHOICE /C YN /N /T 10 /D N /M "Run Report Y or N?"
IF ERRORLEVEL 1 SET REPORT=RunTheReport:
IF ERRORLEVEL 2 SET REPORT=DontRunIt:
ECHO You chose to run %REPORT%
P.S: I replaced the report commands with an echo statement but it is still not working
You have found one of the few instances where the difference between .cmd and .bat is important.
The sample you posted works correctly, when you save that code to a file named script.bat.
script.bat
CHOICE /C YN /N /T 10 /D N /M "Run Report Y or N?"
IF ERRORLEVEL 1 SET REPORT=RunTheReport
IF ERRORLEVEL 2 SET REPORT=DontRunIt
ECHO You chose to run %REPORT%
When the user presses Y the errorlevel is set to 1. The first IF line matches and sets REPORT=RunTheReport. The second IF line does not match, and the end result is Run.
When the user presses N the errorlevel is set to 2. The first IF line matches and sets REPORT=RunTheReport. The second IF line matches and sets REPORT=DontRunIt. The end result is Don't Run.
In a .bat file, the IF ERRORLEVEL ladder will continue and execute every matching SET line. The last matching SET will be the one used.
If you save that same code to a file named script.cmd CMD behaves a little bit differently. One difference is that the SET command now sets ERRORLEVEL to 0 when it successfully sets a variable.
When the user presses Y the errorlevel is set to 1. The first IF line matches and sets REPORT=RunTheReport. The second IF line does not match, and the end result is Run, just like the .bat case.
When the user presses N the errorlevel is set to 2. The first IF line matches, sets REPORT=RunTheReport, and sets ERRORLEVEL to 0. The second IF line then does NOT match. The end result is also Run, which is wrong.
In a .cmd file, only the first matching SET line is run.
Therefore, if your file is named with a .cmd extension, you must reverse the order of the IF ERRORLEVEL lines, so that the correct one is the only one executed.
script.cmd
CHOICE /C YN /N /T 10 /D N /M "Run Report Y or N?"
IF ERRORLEVEL 2 SET REPORT=DontRunIt
IF ERRORLEVEL 1 SET REPORT=RunTheReport
ECHO You chose to run %REPORT%
There is an easy way to avoid this issue and make your code work in both types of file. The IF ERRORLEVEL N syntax is deprecated. It is confusing because it matches as greater-than-or-equal, rather than equal. Instead, use the newer IF %errorlevel% EQU N syntax.
script2.{bat|cmd}
CHOICE /C YN /N /T 10 /D N /M "Run Report Y or N?"
IF %ERRORLEVEL% EQU 1 SET REPORT=RunTheReport
IF %ERRORLEVEL% EQU 2 SET REPORT=DontRunIt
ECHO You chose to run %REPORT%
Save as test.bat and run this script. It works well. Edited to show different approaches possible. It runs a bit faster:
#echo off
CHOICE /C YN /N /T 10 /D N /M "Run Report Y or N?"
IF "%errorlevel%"=="2" (SET "REPORT=DontRunIt:"
) else (SET "REPORT=RunTheReport:")
ECHO You chose to run %REPORT%
timeout 5
exit /b
I have the following code:
set cont=3
:window
start segredo.bat
if %cont% equ 0 goto windowend
set /a cont=cont-1
goto window
:windowend
:loopstart
echo spam
goto loopstart
:loopend
My objective was to open 3 cmd windows and run the echo spam loop in each one of them. Instead, it start opening infinite cmd windows without running loopstart. I'm kind of new to bat language so is there anyone who can help me?
for /l %%a in (1 1 3) do start "" cmd /q /c"for /l %%b in (0) do echo spam"
Inside out
An infinite loop is needed to do the echo, so a simply numeric for /l loop is used. Just a "iterate from 0 to 1 in steps of 0", a for /l %%b in (0 0 1) but abreviated.
As three separate instances are required, the command is placed inside a cmd instance
We use an aditional numeric for /l loop, to start each of the cmd instances. To prevent problems with the commands, as the start command handles the first quoted argument as the window title, a pair of empty quotes are included.
Replace start segredo.bat with call segredo.bat.