Batch: Errorlevels not working in some situations - batch-file

Let me first explain what I am trying to accomplish: I have a GoPro camera and when recording multiple time-lapses you end up with a big pile of img files, two time-lapses are divided by a number in the file name. Gxxxyyyy is how the files are labeled, the y stands for which time-lapse the picture belongs to and x what picture in the time-lapse.
I am trying to create a program that puts every time-lapse in a different folder (sorting by the numbers on the x position); I am using batch because I already had some experience with it.
This is the program I wrote:
#echo off
set loop=0
set fn=1
set session=1
:sort
if %loop% LSS 1000 (
if %fn% GTR 99 (
ROBOCOPY D:\Bureaublad\Output\Pictures D:\Bureaublad\Output\Pictures\Session%session% G%fn%*.JPG
if %ERRORLEVEL% EQU 1 set /A session=%session%+1
)
if %fn% LSS 10 (
ROBOCOPY D:\Bureaublad\Output\Pictures D:\Bureaublad\Output\Pictures\Session%session% G00%fn%*.JPG
if %ERRORLEVEL% EQU 1 set /A session=%session%+1
) ELSE (
if %fn% LSS 100 (
ROBOCOPY D:\Bureaublad\Output\Pictures D:\Bureaublad\Output\Pictures\Session%session% G0%fn%*.JPG
if %ERRORLEVEL% EQU 1 set /A session=%session%+1
)
)
set /A fn=%fn%+1
set /A loop=%loop%+1
goto sort
)
I was trying to use the errorlevel value to determine if the copy was successful, because if it was the next copy had to be to another folder which is what i used the session variable for. But it always copies everything into the "session1" folder and not into separate folders even though i have files with different numbers on the x position.
I tried figuring out what the problem was and used "echo %ERRORLEVEL%" followed by a pause, right after the robocopy commands and every time it just said 0 even though files were copied.
That is the issue and I can't figure out why the errorvalue doesn't change.

The problem is that you try to expand errorlevel inside of a block.
The percent expansion is done by the parser when the block is parsed, before the block is executed.
You should change the expansion to delayed expansion.
#echo off
setlocal EnableDelayedExpansion
set loop=0
set fn=1
set session=1
:sort
if %loop% LSS 1000 (
if !fn! GTR 99 (
ROBOCOPY D:\Bureaublad\Output\Pictures D:\Bureaublad\Output\Pictures\Session!session! G!fn!*.JPG
if !ERRORLEVEL! EQU 1 set /A session=session+1
)
....
)

ERRORLEVEL is a built-in variable, not an environmental variable, and so you shouldn't use the percent signs (%) around it, and simply test directly for the value. The proper way to use it is simply
IF ERRORLEVEL 1 DoSomething

Please see robocopy exit codes
look at this:
Bugs
Version XP026 returns a success errorlevel even when it fails.

Related

Batch file set to pick a random file from preset categories is not picking a random file (keeps picking the last file!)

Good evening. I enjoy modding a game in my free time, it's one those games where individual mods are packed into zip files and loaded in by simply placing said zips into a certain folder. I have hundreds of these zips and was hoping to make a batch that would pick out a random mod for me each time I use it instead of having to pick through them all myself, while placing all the unused ones into a disabled folder that I made to keep everything not being used all in one place. Every mod was prefixed with a name referring to what category it'd belong to, for example 'weapon_shotgun_GiantAngryGun9000.zip' or 'character_huck_Clown.zip'
How this batch script would work is that first it'd take every file with the specified prefixes currently in the mod folder and put them all into the disabled folder. It would then go through every single file in the disabled folder that has all the specified prefixes, pick a random file from each category, and move them into the mod folder. Currently I've gotten my batch to do all that is explained, except rather than pick a random of each, it keeps picking the alphabetically last one from each prefix list. I believe it's because the Num var being used to designate the index for the entries in the File array is not being modified or read, and in-spite my best efforts I cannot get it to work the way I'm hoping.
Any advice or solutions is appreciated, thank you for reading.
setlocal EnableDelayedExpansion
SET modFolder="C:\Game\Mods\"
SET disabledFolder="C:\Game\Mods\Disabled\"
SET itemListIndex=0
::Prefix names
SET Prefix1=Weapon_Shotgun_
set itemList[%itemListIndex%]=%Prefix1%
set /A itemListIndex+=1
SET Prefix2=Character_ThisPerson_
set itemList[%itemListIndex%]=%Prefix2%
set /A itemListIndex+=1
SET Prefix3=Weapon_Rifle_
set itemList[%itemListIndex%]=%Prefix3%
set /A itemListIndex+=1
for /l %%G in (0 1 %itemListIndex%) do (
if not "!itemList[%%G]!" == "" (
set num=0
for %%f in (!itemList[%%G]!*.zip) do (
set /A num+=1
set file[%num%]=%%f
)
set /A pick = !random! * %num% / 32768 + 1
echo Moving "!file[%pick%]!" to the mod folder
move "%disabledFolder%!file[%pick%]!" %modFolder%
)
)
set file[%num%]=%%f is echoed as set file[]='[name of file]' instead of the expected set file['(index number)']='[name of file]'
the written echo says Moving !file[]! to the mod folder instead of the expected Moving (name of file) to the mod folder
The pick variable echos as blank, implying num or random are not valid
#ECHO OFF
setlocal EnableDelayedExpansion
SET modFolder="C:\Game\Mods\"
SET disabledFolder="C:\Game\Mods\Disabled\"
SET itemListIndex=0
::Prefix names
SET Prefix1=Weapon_Shotgun_
set itemList[%itemListIndex%]=%Prefix1%
set /A itemListIndex+=1
SET Prefix2=Character_ThisPerson_
set itemList[%itemListIndex%]=%Prefix2%
set /A itemListIndex+=1
SET Prefix3=Weapon_Rifle_
set itemList[%itemListIndex%]=%Prefix3%
set /A itemListIndex+=1
for /l %%G in (0 1 %itemListIndex%) do (
if not "!itemList[%%G]!" == "" (
set num=0
for %%f in (!itemList[%%G]!*.zip) do (
set /A num+=1
set file[!num!]=%%f
)
set /A pick = !random! * !num! / 32768 + 1
CALL SET "PICKFILE=%%file[!pick!]%%"
REM echo Moving "!file[%pick%]!" to the mod folder
REM ECHO move "%disabledFolder%!file[%pick%]!" %modFolder%
echo Moving "!pickfile!" to the mod folder
ECHO move "%disabledFolder%!pickfile!" %modFolder%
)
)
GOTO :EOF
Your issue is that %num% and %pick% are the values of num and ``pick` when the outer loop (%%G) was parsed, that is, nothing.
The classic method is to use !num! for the run-time value, but you are using indirection, so you'd be tempted to use !file[!pick!]! which is ambiguous and resolved to pick. The call uses a subshell to resolve the ambiguity.
Move command disarmed by being echoed.
Despite having already received and accepted an answer, I have decided to post this to show you how I would do it.
#Echo Off
SetLocal EnableDelayedExpansion
Set "modFolder=C:\Game\Mods"
Set "disabledFolder=C:\Game\Mods\Disabled"
Rem Undefine existing variables
For /F "Delims==" %%A In ('Set item 2^>NUL') Do Set "%%A="
Rem Define filename prefixes
Set "itemListIndex=0"
For %%A In (
Weapon_Shotgun
Character_ThisPerson
Weapon_Rifle
) Do (Set /A itemListIndex+=1
Set "itemList[!itemListIndex!]=%%A_")
Rem Randomize itemListIndex
Set /A itemListIndex=(%RANDOM% %% itemListIndex)+1
Rem Define files based upon itemList[%itemListIndex%]
Set "itemFileIndex=0"
For %%A In ("!itemList[%itemListIndex%]!*.zip") Do (Set /A itemFileIndex+=1
Set "itemFile[!itemFileIndex!]=%%A")
Rem Randomize itemFileIndex
Set /A itemFileIndex=(%RANDOM% %% itemFileIndex)+1
Rem Move itemFile[%itemFileIndex%] to modFolder.
Echo Moving "!itemFile[%itemFileIndex%]!" to the mod folder
Move /Y "%disabledFolder%\!itemFile[%itemFileIndex%]!" "%modFolder%"
Pause
This way, you can easily append to the parenthesized list of prefixes, (which both significantly reduces your code and makes it simpler to maintain). It also makes more sense to randomize itemListIndex first, to reduce the number of returned .zip files for selection/moving.

Batch file quitting after doing "call :loop"

I have a bit of (Bad) Code for encrypting text, but to be able to decrypt it needs to have something inbetween the numbers. I want to fit random letters inbetween the numbers so it looks less obvious, this is where i got to:
#echo off
setlocal enableDelayedExpansion
set /p code=Text:
set chars=0123456789abcdefghijklmnopqrstuvwxyz
for /L %%N in (10 1 36) do (
for /F %%C in ("!chars:~%%N,1!") do (
Set _Alphanumeric=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
Set _count=0
set _RNDLen=%random%
Set /A _RNDLen=_RNDLen%%4
If !_count! leq %_RNDLen% call :loop
set "code=!code:%%C=-%%N!"
)
)
echo !code!
echo !_str!
pause
:loop
Set /a _count+=1
set _RND=%random%
Set /A _RND=_RND%%51
SET _str=!_str!!_Alphanumeric:~%_RND%,1!
EXIT /B %ERRORLEVEL%
The problem is that the program just quits before giving any output, even if i remove the exit /b statement. Thanks for help
I've no idea what principle you're using for your algorithm, but fundamentally you need to understand delayed expansion.
When your outer loop, for %%N is parsed, every %var% is replaced by the contents of that variable at that time, hence
set _RNDLen=%random%
If !_count! leq %_RNDLen% call :loop
are replaed by
set _RNDLen=a specific random number
If !_count! leq call :loop
The first line here will set _rndlen to the same number every time (for ny run) and since _rndlen is undefined at the start of the loop, it willl be replaced by nothing, hence the if statement has faulty syntax and hence cmd objects and would display a message.
You can use !random! with delayed expansion invoked to select a rendom number each time, and you need !_rndlen! to access the changed value of _rndlen (changed from its original value of nothing to some random value and then mod-4'd)
Personally, I'd assign _alphanumeric outside of the (outer) loop since its value isn't varied by the loop operation.
And naturally, you know that when you hit Return following the pause, the loop code will be executed before the routine terminates (by flow-through) and you should include a
goto :eof
line after the pause to skip this last operation.

How to increment for loop variable in batch script?

::Compare with available valid arguments
FOR /L %%i IN (1,1,!avArgc!) DO (
FOR /L %%j IN (1,1,!argc!) DO (
IF !avArgv[%%i]!==!argv[%%j]! (
echo Match: !avArgv[%%i]!
::Check the next option
SET /A nextArg=%%j
SET /A nextArg+=1
FOR /L %%n IN (!nextArg!,1,!nextArg!) DO (
IF !nextArg! LEQ !argc! (
echo next arg: !argv[%%n]!
call :CheckSubOption
)
)
)
)
)
In my above code example - How do I take for loop variable like %%j and increment itself within the for loop like this %%j++ ? Current solution that I have (which is messy and I don't like it) is to create a new variable and set it to the value of %%j and then increment that variable and start using that variable like this:
::Check the next option
SET /A nextArg=%%j
SET /A nextArg+=1
Observing your code and your intention, it would seem that you would want to skip numbers during the loop structure. The way you want to change it though would be destabilizing. In most scripting languages such as matlab,bash, and batch, the variable that is used in for-loops serves as a frame of reference within the loop. When you tell the code to run a particular for-loop, it will run that computation regardless if the parameters of it changed. A real world example of this is the professor who is using outdated figures to solve a problem and it isnt until the next day he receives the new figures. The professor cant change his answer accordingly because he doesnt have the new data yet.
This does not mean this problem is unsolvable. In fact there are a variety of ways to approach this. The first one which is a little more complicated involves a nested For structure.
#echo off
set /p maxLength=[Hi times?]
set skip=0
FOR /L %%i IN (1,1,%maxLength%) DO (call :subroutine %%i)
echo alright im done.
pause
GOTO :eof
rem the below code uses a for loop structure that only loops 1 time based on the passed argument from the overall for loop as so to make changes to how its run.
:subroutine
set /a next=%1+%skip%
FOR /L %%r IN (%next%,1,%next%+1) DO (call :routine %%r)
GOTO :eof
:routine
if %1==3 (set /a skip=1)
echo %skip%
echo %next%
echo %1
pause
GOTO :eof
When running the program, the variable next will skip the value of 3 if the maxlength variable is greater than 3.
The reason this is so is because the nested for-loop only runs once
per iteration of the overall for loop
. This gives the program time to reset the data it uses, thanks to the call command which serves as a way to update the variables. This however is extremely inefficient and can be done in much less lines of code.
The second example uses GOTO's and if statements.
#echo off
set jump=1
:heyman
set /A "x+=%jump%"
if %x%==4 (set /A "jump=2")
echo %x%
if %x% LSS 10 goto heyman
echo done!
This code will essentially echo the value of x thats incremented each time until it reaches the value of 10. However when it reaches 4, the increment increases by 1 so each time it runs the loop increments the x value by 2. From what you wanted, you wanted to be able to change the way the value of %%j increments, which can not be done as %%j is a statement of where the for-loop is in its computation. There is no difference in what can be accomplished with for-loops and goto statements except in how they are handled.
While i unfortunately don't have the correct form of your code yet, i know that code examples i have given can be utilized to achieve your particular desire.
The general solution for thoses case is to not rely on blocks inside loops/if but instead to use subroutines where you are not blocked by the level of evaluation.
FOR /L %%i IN (1,1,!avArgc!) DO call :Loop1 %%i
goto :EOF
:Loop1
FOR /L %%j IN (1,1,!argc!) DO call :Loop2 %1 %%j
goto :EOF
:Loop2
IF !avArgv[%1]!==!argv[%2]! (
echo Match: !avArgv[%1]!
::Check the next option
SET /A nextArg=%2+1
call :CheckOpt %nextArg%
)
goto :EOF
:CheckOpt
IF %1 LEQ %argc% (
echo next arg: !argv[%1]!
call :CheckSubOption
)

'for' loop variable not releasing on loop iterations

Been wrecking my brain all night trying to figure out why this isn't working, but one of my variables isn't releasing on the next iteration of my loop and I can't figure out why... The first pass of the loop seems to work fine, but the next iteration, the first variable gets locked and the script connects to the system that's already been configured.
I've been staring at this for a while now and no matter how I approach it, it still behaves badly. :/ The purpose is to read a text-string of a given file, and use it to modify (via Find and Replace (fnr.exe)) another file with several instances of the required data. I didn't have alot of luck with 'findstr' replacing so many instances of the text required so I went with a tool I've used before that seemed to work really well in it's previous scripting application...
Truth be told, I find myself stumbling with even the most basic code a lot of times, so any kind soul willing to impart some wisdom/assistance would be greatly appreciated!
Thanks in advance...
#ECHO ON
setlocal enabledelayedexpansion
> "%~dp0report.log" ECHO Batch Script executed on %DATE% at %TIME%
rem read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
SET lwn=
SET WKSTN=%%A
rem connect to workstation and read lwn.txt file
pushd "\\%WKSTN%\c$\"
IF ERRORLEVEL 0 (
FOR /F %%I in (\\%wkstn%\c$\support\lwn.txt) DO (
SET LWN=%%I
%~dp0fnr.exe --cl --dir "\\%WKSTN%\c$\support\folder\config" --fileMask "file.xml" --find "21XXXX" --replace "%%I"
IF ERRORLEVEL 0 ECHO Station %LWN%,Workstation %WKSTN%,Completed Successfully >> %~dp0report.log
IF ERRORLEVEL 1 ECHO Station %LWN%,Workstation %WKSTN%, A READ/WRITE ERROR OCCURRED >> %~dp0report.log
echo logwrite error 1 complete
popd
)
)
IF ERRORLEVEL 1 (
ECHO ,,SYSTEM IS OFFLINE >> %~dp0report.log
)
popd
set wkstn=
set lwn=
echo pop d complete
)
msg %username% Script run complete...
eof
The ! notation must be used on all variables that are changed inside the loop.
C:>type looptest.bat
#ECHO OFF
setlocal enabledelayedexpansion
rem read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
SET WKSTN=%%A
ECHO WKSTN is set to %WKSTN%
ECHO WKSTN is set to !WKSTN!
pushd "\\!WKSTN!\c$\"
ECHO After PUSHD, ERRORLEVEL is set to %ERRORLEVEL%
ECHO After PUSHD, ERRORLEVEL is set to !ERRORLEVEL!
IF !ERRORLEVEL! NEQ 0 (
ECHO ,,SYSTEM IS OFFLINE
) ELSE (
ECHO Host !WKSTN! is available
)
popd
)
EXIT /B 0
The workstations.txt file contained the following. (I should not give out actual host names.)
LIVEHOST1
DEADHOST1
LIVEHOST2
The output is...
C:>call looptest.bat
WKSTN is set to
WKSTN is set to LIVEHOST1
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 0
Host LIVEHOST1 is available
WKSTN is set to
WKSTN is set to DEADHOST1
The network path was not found.
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 1
,,SYSTEM IS OFFLINE
WKSTN is set to
WKSTN is set to LIVEHOST2
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 0
Host LIVEHOST2 is available
Although your code have several issues, the main one is the use of % instead of ! when you access the value of variables modified inside a for loop (although you already have the "enabledelayedexpansion" part in setlocal command). However, I noted that you sometimes use the FOR replaceable parameter (like in --replace "%%I") and sometimes you use the variable with the same value (%LWN%), so a simpler solution in your case would be to replace every %VAR% with its corresponding %%A for parameter.
I inserted this modification in your code besides a couple small changes that make the code simpler and clearer.
#ECHO ON
setlocal
> "%~dp0report.log" ECHO Batch Script executed on %DATE% at %TIME%
rem Read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
rem Connect to workstation and read lwn.txt file
pushd "\\%%A\c$\"
IF NOT ERRORLEVEL 1 (
FOR /F "usebackq" %%I in ("\\%%A\c$\support\lwn.txt") DO (
%~dp0fnr.exe --cl --dir "\\%%A\c$\support\folder\config" --fileMask "file.xml" --find "21XXXX" --replace "%%I"
IF NOT ERRORLEVEL 1 (
ECHO Station %%I,Workstation %%A,Completed Successfully >> %~dp0report.log
) ELSE (
ECHO Station %%I,Workstation %%A, A READ/WRITE ERROR OCCURRED >> %~dp0report.log
echo logwrite error 1 complete
)
)
) ELSE (
ECHO ,,SYSTEM IS OFFLINE >> %~dp0report.log
)
popd
echo pop d complete
)
msg %username% Script run complete...

Batch: Replacing number in text (replay script) in loop

can somebody help me with my problem, please?
I am trying to make a batch file, which will do several loops (with ANSYS commands). Problem here is that I need to change numbers in ANSYS scripts according to current loop (Results from ANSYS are always with increasing number - results01.res, results02.res etc). So in the FIRST loop I need to load to ANSYS results01.res, in SECOND loop results02.res etc, but when This "loading" is done by ANSYS script, where I need to change the number of loading file.
I found several tips for replacing text in txt file. Best of them is referenced here:
http://www.dostips.com/DtCodeBatchFiles.php#Batch.FindAndReplace
Problem is, that when I want to change number, instead of word, this is not working. Here is my batch:
#echo off
set /a "x=0"
set /a "y=1"
:do_while_loop_start
echo %x% loop
set /a "x=x+1"
echo %y%
set /a "y=y+1"
call rename.bat "results0%y%.res" "results0%x%.res changeNumber.txt>newfile.txt
IF %x%==20 (echo %x% equal to 20
) ELSE (
goto do_while_loop_start
)
:do_while_loop_end
Desired output is to "rename" result01.res to result02.res
Look at the output what's going on and remove the echo and ^ if it is OK:
#echo off &setlocal
set /a x=0
set /a y=1
:do_while_loop_start
echo %x% loop
set /a x=x+=1
echo %y%
set /a y=y+=1
echo call rename.bat results0%x%.res results0%y%.res changeNumber.txt^>newfile.txt
IF %x%==20 (echo %x% equal to 20
) ELSE (
goto do_while_loop_start
)
:do_while_loop_end
pause
You must note that if you do the renames in ascending order, at end all names will be renamed to the last name. For example:
First loop: result01.res -> result02.res
Second loop: previously renamed result02.res and other result02.res -> result03.res
Third loop: all result03.res renamed to result04.res
etc...
In last loop: all previously renamed names -> result20.res
To solve this problem, the renames must be achieved in descending order.
On the other hand, you should accumulate the rename of previous cycle into the next one, otherwise the final result is the same than if just the last loop would be executed. You must move the newfile into the original one after each loop.
Finally, your code have a problem when the number have 2 digits.
#echo off
set /A x=120, y=121
:do_while_loop_start
echo Rename %x:~-2% to %y:~-2%
call rename.bat "results%x:~-2%.res" "results%y:~-2%.res" changeNumber.txt > newfile.txt
move /Y newfile.txt changeNumber.txt
set /A x-=1, y-=1
IF %x% gtr 100 goto do_while_loop_start
echo End of loop
:do_while_loop_end
Be sure to backup your original changeNumber.txt file before testing this program.

Resources