I'm trying to set a variable every time + 1 with a batch file. So when the batch file opens it calls the file with the var and then redefines it plus 1. But when i open the file and then open count.bat i see this(in count.bat): set backupcount= instead of
set backupcount=1 which it should be (1 can also be 2, 3, 4, 5, enz).
This is the code i'm using:
#echo off
if exist "backup-tool\count.bat" call "backup-tool\count.bat"
if not exist "backup-tool\count.bat" echo set backupcount=0 > "backup-tool\count.bat"
call "backup-tool\count.bat"
if "%backupcount%"=="8" (
echo set backupcount=1 > "backup-tool\count.bat"
) else (
set /a "backupcount=backupcount+=1"
echo set backupcount=%backupcount% > "backup-tool\count.bat"
)
pause >nul
Anyone that knows what i'm doing wrong and tell me how i should do it?
All help is very much appreciated!
This one needs to use setlocal ENABLEDELAYEDEXPANSION, because you work in a block of code where you manipulate that variable and you want to use it right there(not the variable that is out of the block). This should work:
#echo off
setlocal ENABLEDELAYEDEXPANSION
if exist "backup-tool\count.bat" call "backup-tool\count.bat"
if not exist "backup-tool\count.bat" echo set backupcount=0 > "backup-tool\count.bat"
call "backup-tool\count.bat"
if "!backupcount!"=="8" (
echo set backupcount=1 > "backup-tool\count.bat"
) else (
set /a "backupcount=backupcount+=1"
echo set backupcount=!backupcount! > "backup-tool\count.bat"
)
pause >nul
Your original problem is related to Delayed Expansion as other answer said; however, your code is also unnecessarily complex. This is the way I would do it:
#echo off
if exist "backup-tool\count.bat" call "backup-tool\count.bat"
set /A "backupcount=backupcount%%8+1"
echo set "backupcount=%backupcount%" > "backup-tool\count.bat"
The set /A command takes as zero the value of any non-existent variable, so it is not necessary to initialize it with zero when the data file not exists.
If you want a repeating count from 1 to 8 and then reset the counter to 1, you may use the % Modulus operator in a simpler way that don't require an if. Type set /? for further details on %% operator, or see this Wikipedia article.
EDIT: Additional explanations added
The first time the program run the backupcount variable does not exist, so the set /A "backupcount=backupcount%%8+1" expression generate a 1 that is stored in the file. You may also add a set backupcount=0 command before the if just to avoid problems with previous executions of the same Batch file (or add a setlocal command at beginning).
The next time this variable is initialized with 1, so set /A "backupcount=backupcount%%8+1" expression produce a 2. The same happen with next numbers up to 8.
When the variable is initialized with 8 the expression backupcount%%8, that is the remainder when the variable is divided by 8, is zero; so the whole expression produce a 1 again.
Related
When I do this in Notepad, the command prompt doesn't show ping %ip% -t -l %package%, but it shows ping %ip% -t -l and doesn't show the package variable.
#echo off
set data=0
set package = -600
IF %data% == 0 (
set /a package= %package% + 1600
#echo ping %ip% -t -l %package%
)
echo %package%
pause
What am I doing wrong?
Batch is sensitive to spaces in a SET statement. SET FLAG = N sets a variable named "FLAGSpace" to a value of "SpaceN"
The set "var=value" syntax ensures that any trailing spaces on the batch line are not included in the value assigned to var.
Within a block statement (a parenthesised series of statements), the entire block is parsed and then executed. Any %var% within the block will be replaced by that variable's value at the time the block is parsed - before the block is executed - the same thing applies to a FOR ... DO (block).
Hence, IF (something) else (somethingelse) will be executed using the values of %variables% at the time the IF is encountered.
Two common ways to overcome this are 1) to use setlocal enabledelayedexpansion and use !var! in place of %var% to access the changed value of var or 2) to call a subroutine to perform further processing using the changed values.
Note therefore the use of CALL ECHO %%var%% which displays the changed value of var.
So:
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set /A data=0
set /A package=-600
IF %data% == 0 (
set /a package=!package!+1600
echo ping %ip% -t -l !package!
)
echo %package%
pause
Noting: The setlocal statement should normally be placed at the start of the code. Your posted code is evidently a snip, since you do not appear to be setting ip.
Spaces are irrelevant in a set /a but even so, removing them fosters a habit
Set /a uses the run-time value, not the parse-time value of var when the syntax set /a var=var+1 is used within a loop, so set /a var=var+1 and set /a var=!var!+1 are equivalent but set /a var=%var%+1 uses the value of var at the time the loop is parsed.
Since echoing is set to off by the initial statement, the leading # on the second echo is redundant.
I am trying to store a concatenated set of variables into a newly SET variable. When I add a variable into another variable it doesn't seem to actually get set correctly. I'm curious if BATCH can store variables within variables, or if I have to do formatting beyond what I currently have:
Example: The 'oldDirectory' variable should display the same thing as "%progdata%\%datetime%"
#echo off
For /f "tokens=2-4 delims=/ " %%a in ("%DATE%") do (
SET YYYY=%%c
SET MM=%%a
SET DD=%%b
)
For /f "tokens=1-3 delims=/:." %%a in ("%TIME%") do (
SET HH24=%%a
SET MI=%%b
SET SS=%%c
)
SET datetime=%YYYY%%MM%%DD%_%HH24%%MI%%SS%
SET progdata=C:\ProgramData
#echo on
IF EXIST "%progdata%" (
echo Found %progdata%
SET oldDirectory="%progdata%\%datetime%"
echo %oldDirectory%
)
pause
try with :
CALL SET oldDirectory="%progdata%\%datetime%"
CALL ECHO %oldDirectory%
First method:
IF EXIST "%progdata%" (
echo Found %progdata%
SET oldDirectory="%%progdata%%\%%datetime%%"
call echo %oldDirectory%
)
Second method:
IF EXIST "%progdata%" (
echo Found %progdata%
SET oldDirectory="!progdata!\!datetime!"
setlocal EnableDelayedExpansion
echo %oldDirectory%
)
An interesting point is that echo %oldDirectory% command display the current value of progdata and datetime variables with the same value of oldDirectory!
EDIT: Example added
#echo off
set progdata=C:\ProgramData
echo First method:
SET oldDirectory="%%progdata%%\%%date:/=%%_%%time::=%%"
call echo %oldDirectory%
echo Second method:
SET oldDirectory="!progdata!\!date:/=!_!time::=!"
setlocal EnableDelayedExpansion
echo %oldDirectory%
Output:
First method:
"C:\ProgramData\14082013_211303.20"
Second method:
"C:\ProgramData\14082013_211303.21"
You have a standard DELAYED EXPANSION problem, discussed endlessly on SO.
When batch encounters a "Block statement" - that is typically a parenthesised statement spread over many lines such as your IF EXIST then the entire statement is parsed through to the closing parenthesis AND at this time, ANY %var% is replaced by the value of that variable as it stands WHEN THE STATEMENT IS PARSED
Consequently, your ECHO %olddirectory% is replaced by ECHO since olddirectory has no value AT PARSE TIME and executing ECHO will report ECHO is On/Off progdata on the other hand IS set at parse-time and hence echo Found %progdata% is replaced by echo Found C:\ProgramData
The very simplest cure is to move the ECHO statement outside of the block
IF EXIST "%progdata%" (
echo Found %progdata%
SET oldDirectory="%progdata%\%datetime%"
)
echo Olddirectory=%oldDirectory%
(I added the olddirectory= so that the echo statement finds something to echo if olddirectory is not set)
The second easiest way to display the value is
IF EXIST "%progdata%" (
echo Found %progdata%
SET oldDirectory="%progdata%\%datetime%"
CALL echo %%oldDirectory%%
)
Here, the ECHO command is not expanded in the context of the IF, but in the context of the CALL which acquires its environment from the run-time value of the IF context.
The third easiest way to display the value is by using the delayedexpansion option of a setlocal command. An NT batch command traditionally starts
#echo off
setlocal
which suppresses echoing and establishes a local environment. Any changes to the local environment are backed out when an endlocal or end-of-file is reached in the setlocal's context. If this mantra is consistently followed, we don't get the situation where a variable is established by one batch and the environment is 'dirty' for the next. Consider running your original twice within the same cmd session. progdata, and all of the other variables you are establishing would remain set for the second coming - and hence olddirectory may be set by your first invocation, and retain that stale data if for some reason it's not EXPLICITLY set in the second. setlocal backs all those changes out for you.
setlocal enabledelayedexpansion adds an extra facility to the mix. Whereas %var% is resolved to the PARSE-TIME value of var, if delayedexpansion has been invoked then !var! is resolved to the RUN-TIME value - as it changes in a FOR loop...
Hence, adding
SETLOCAL ENABLEDELAYEDEXPANSION
at a strategic point (after the #echo off until you're off your training wheels...) would allow you to make a simple change to the display of olddirectory
IF EXIST "%progdata%" (
echo Found %progdata%
SET oldDirectory="%progdata%\%datetime%"
echo !oldDirectory!
)
I am trying to get nested variables in my batch game i am creating.
I want it so that it will choose a random variable and change it to X, but if it is already chosen, it should go back and choose a different number.
set 1=a
set 2=b
set 3=c
set 4=d
set 5=e
those were the variables, here is the code
setlocal enabledelayedexpansion
:eliminator
set /a eliminate=(%random * 5) / 32767 + 1
if %%eliminate%%==X goto eliminator
echo The letter !!eliminate!! was chosen
timeout 5
set %%eliminate%%=X
goto eliminator
Now, the thing is, when I try to echo it, it writes the name of the variable instead of the value. Also, variables that have already been chosen are being chosen again. Any way I could fix this? Thanks.
try this:
#echo off&setlocal
set "var1=a"
set "var2=b"
set "var3=c"
set "var4=d"
set "var5=e"
:loop
set /a rd=%random%%%5+1
if defined var%rd% call echo %%var%rd%%%
set "var%rd%="
set "var" >nul 2>&1 && goto:loop
..output (may vary):
d
a
c
b
e
Your posted code is missing the closing % around random - it should read %random%.
Your formula for a random number between 1 and 5 is more complicated than need be. I would use:
set /a eliminate=%random% %% 5 + 1
To expand a "nested variable" you need !%eliminate%!
But I would completely rewrite your algorithm. I think the following does what you want:
#echo off
setlocal enableDelayedExpansion
set "chars=abcde"
set charCnt=5
:loop
set /a "pos=%random% %% charCnt, pos2=pos+1, charCnt-=1"
set "letter=!chars:~%pos%,1!"
echo The letter %letter% was chosen
set "chars=!chars:~0,%pos%!!chars:~%pos2%!"
if defined chars goto loop
The script is optimized to always pick a valid unused letter on each iteration.
I have written a batch file that is launched as a post processing utility by a program. The batch file reads ~24 parameters supplied by the calling program, stores them into variables, and then writes them to various text files.
Since the max input variable in CMD is %9, it's necessary to use the 'shift' command to repeatedly read and store these individually to named variables. Because the program outputs several similar batch files, the result is opening several CMD windows sequentially, assigning variables and writing data files. This ties up the calling program for too long.
It occurs to me that I could free up the calling program much faster if maybe there's a way to write a very simple batch file that can write all the command parameters to a text file, where I can process them later. Basically, just grab the parameter list, write it and done.
Q: Is there some way to treat an entire series of parameter data as one big text string and write it to one big variable... and then echo the whole big thing to one text file? Then later read the string into %n variables when there's no program waiting to resume?
Parameter list is something like 25 - 30 words, less than 200 characters.
Sample parameter list:
"First Name" "Lastname" "123 Steet Name Way" "Cityname" ST 12345 1004968 06/01/2010 "Firstname+Lastname" 101738 "On Account" 20.67 xy-1z 1 8.95 3.00 1.39 0 0 239 8.95
Items in quotes are processed as string variables. List is space delimited.
Any suggestions?
echo %* 1>args.txt
%* references all arguments: %1 %2 %3...
It also works with subroutines.
call :test 1 2 3
goto :eof
:test
echo 1: %1
echo 2: %2
echo 3: %3
echo *: %*
exit /b
output:
1: 1
2: 2
3: 3
*: 1 2 3
See the following website for more information:
http://ss64.com/nt/syntax-args.html
Interesting Post. It sparked my interest.
I too am needing something that could accept parameters and although this probably isn't useful to you now I thought it might be useful at some later date.
My solution is less simple - because there just isn't an elegant way to do it.
Basically, in this example the "-" can be used to identify a parameter, and the next space is assumed to be set to a value.
Legal Stuff:
So this is all my code and I don't really care how or where you choose to use it. No need to cite me it's just an example anyway.
Like this:
Microsoft Batch:Begin Copy below and save as filename.bat
#ECHO OFF
REM USAGE: this-batch-name.bat -BUILD "1.2.3 build 405" -JOB "Running This Job" -run RUN_FUNCTION
SET __CURRENT_WORKING_DIRECTORY__=%~dp1
ECHO.__CURRENT_WORKING_DIRECTORY__=%__CURRENT_WORKING_DIRECTORY__%
REM # Clear Previous Variables
SET PACKAGING_BUILD_NUMBER=
SET PACKAGING_JOB_NAME=
SET GO_DEEPER=
SET RUN_COMMAND=
REM ## In order to read variables set while in a "FOR" loop
REM ## you have to set the 'ENABLEDELAYEDEXPANSION' with 'SETLOCAL'.
SETLOCAL ENABLEEXTENSIONS
SETLOCAL ENABLEDELAYEDEXPANSION
REM ## Capture Command line parameters here with a %*
FOR %%A IN (%*) DO (
REM ## If we found something with a '-' in previous pass run GO_DEEPER will be defined and thus set to the command line argument.
IF DEFINED GO_DEEPER (
REM ## When ENABLEDELAYEDEXPANSION is Set with setlocal command you have to use exclamation: i.e. '^!'
IF /I "-BUILD"=="!GO_DEEPER!" SET PACKAGING_BUILD_NUMBER=%%A
IF /I "-JOB"=="!GO_DEEPER!" SET PACKAGING_JOB_NAME=%%A
IF /I "-RUN"=="!GO_DEEPER!" SET RUN_COMMAND=%%A
SET SET GO_DEEPER=
)
IF /I "%%A" GEQ "-" (
REM ## Wow we found your command line argument that started with a '-' so set the GO_DEEPER Var
SET GO_DEEPER=%%A
) ELSE (
SET SET GO_DEEPER=
)
)
REM ## Time to grab the variables set while in delayed expansion mode
ENDLOCAL && SET PACKAGING_BUILD_NUMBER=%PACKAGING_BUILD_NUMBER% && SET PACKAGING_JOB_NAME=%PACKAGING_JOB_NAME% && SET RUN_COMMAND=%RUN_COMMAND%
REM ## Sucks, but you have to clear the '"' and "'" if it exists.
IF DEFINED RUN_COMMAND (
SET RUN_COMMAND=%RUN_COMMAND:"=%
SET RUN_COMMAND=%RUN_COMMAND:'=%
)
IF DEFINED PACKAGING_JOB_NAME (
SET PACKAGING_JOB_NAME=%PACKAGING_JOB_NAME:"=%
SET PACKAGING_JOB_NAME=%PACKAGING_JOB_NAME:'=%
)
IF DEFINED PACKAGING_BUILD_NUMBER (
SET PACKAGING_BUILD_NUMBER=%PACKAGING_BUILD_NUMBER:"=%
SET PACKAGING_BUILD_NUMBER=%PACKAGING_BUILD_NUMBER:'=%
)
REM ## Now we can try to run the command function if the -run was used...
IF DEFINED RUN_COMMAND (
CALL:--%RUN_COMMAND% "'%PACKAGING_JOB_NAME%'","'%PACKAGING_BUILD_NUMBER%'"
) ELSE (
ECHO Try running:
ECHO %0 -BUILD "1.2.3 build 405" -JOB "Running This Job" -run RUN_FUNCTION
)
GOTO DONE
:--RUN_FUNCTION
ECHO running... %~0
SET VARPASSED1=%~1
SET VARPASSED2=%~2
IF DEFINED VARPASSED1 ECHO VARPASSED1 was %VARPASSED1%
IF DEFINED VARPASSED2 ECHO VARPASSED2 was %VARPASSED2%
ECHO Add your code to process here...
GOTO:EOF
:DONE
ECHO We got the following results...
IF DEFINED PACKAGING_JOB_NAME ECHO PACKAGING_JOB_NAME=%PACKAGING_JOB_NAME%
IF DEFINED PACKAGING_BUILD_NUMBER ECHO PACKAGING_BUILD_NUMBER=%PACKAGING_BUILD_NUMBER%
IF DEFINED RUN_COMMAND ECHO RUN_COMMAND=%RUN_COMMAND%
</pre> </code>
Microsoft Batch END Copy
RESULTS:
__CURRENT_WORKING_DIRECTORY__=C:\dev\a\win\sysprep\
running... :--RUN_FUNCTION
VARPASSED1 was "'Running...'"
VARPASSED2 was "'This...'"
We got the following results...
PACKAGING_JOB_NAME="Running This Job"
PACKAGING_BUILD_NUMBER="1.2.3 build 405"
RUN_COMMAND=RUN_FUNCTION
How can I count how many calls of a cmd file?
I'm struggling with something like this but it didn't work:
#IF NOT EXIST Calls.log echo. > Calls.log
#for %%i in (Calls.log) do set size=%%~zi
#IF %size% EQU 0 (
#ECHO 1 > Calls.log
) ELSE (
#set /p v=<Calls.log
#set /A v+=1
#echo %v% > Calls.log
)
If all you're trying to do is count how many times a cmd script is called, you can just append one character to a file every time it runs rather than fiddling around with expression evaluation every time the script is run. This also has the advantage of making the script quicker since the analysis of the count is moved elsewhere.
The counter file expands by one byte every time, so watch out if you're calling it a truly large number of times since the file will expand. But even, calling it once per second, a 1G file will accrue only after 30 years.
At the top of your script, just put:
set >>countfile.txt <nul: /p x=X
This simply adds the character X to the end of the countfile.txt file every time the script is called. It uses the "set/p" command with input/output redirection, which is the only way I'm aware of to get a character out without a CR/LF following it (like the UNIX "echo -n").
To get a count of the number of calls to date, you can use the file size environment variable modifier, as in the following script (I expect this will be done less often than running the script so it's better to put the grunt work here [in fact, it's not a lot of grunt work since it's not counting the characters, rather it gets the information directly from the directory entry]):
#echo off
goto :main
:getsize
echo %~z1
goto :eof
:main
setlocal enableextensions enabledelayedexpansion
call :getsize countfile.txt
endlocal
To reset the count, use the following extremely complicated command (I'm thinking of patenting this):
del countfile.txt
One other thing I'd suggest - you don't need to prefix every command with "#" to prevent echo, you can simply put "#echo off" at the top of your script for global no-echo. If you want to selectively echo some commands, just ignore this paragraph.
The complete if block is parsed at once and thus all environment variables in it are getting replaced by their values at the time before the if block gets executes. You need to enable delayed variable expansion and use !v!:
#setlocal enabledelayedexpansion
#IF NOT EXIST Calls.log echo. > Calls.log
#for %%i in (Calls.log) do set size=%%~zi
#IF %size% EQU 0 (
#ECHO 1 > Calls.log
) ELSE (
#set /p v=<Calls.log
#set /A v+=1
#echo !v! > Calls.log
)
And you can simplify the code as follows:
#echo off
setlocal enabledelayedexpansion
IF NOT EXIST Calls.log (
ECHO 1 > Calls.log
) ELSE (
set /p v=<Calls.log
set /A v+=1
echo !v! > Calls.log
)
There is no need to create the file beforehand (and even that I'd solve with copy nul Calls.log, since that ensures a file size of 0).
The following code works on my computer:
#if not exist Calls.log (
echo 0 > Calls.log
)
#set /p v=< Calls.log
#set /a v=v+1
echo %v% > Calls.log