Windows Batch File Multiple labels back to back - windows

In PHP or Javascript and other languages as well, there is the switch, case statement as a control flow tool. One of the neat features of that is it allows for multiple cases to be pointed to a single command group. For example:
switch(abc) {
case a:
case b:
case false:
console.log("hi")
break;
case c:
console.log("see ya")
break;
etc...
}
So that if abc is equal to a or b or is false, the "Hi" will be logged. Depending on the code, it can be a lot cleaner than calling from an object or tons of if else or if x || y || z statements.
I have a windows batch file where I'm doing the following:
GOTO %1
..... stuff
.... more stuff
REM =================LABELS BELOW==============
:-h
:--help
:-?
:--?
type help.txt
exit /b
It's more detailed than the above pseudocode, but that's the gist of it. It allows for aliases for the same argument And it works. If I execute mycmd -h or mycmd --help etc., I help the help file text displayed on the screen.
However, on the last line of my output, I get the error, THE SYSTEM CANNOT FIND THE BATCH LABEL SPECIFIED -.
The error might be caused by something else. I have some CALL commands and GOTO :EOF statements, so that certainly could be the source of the error.
But I've never seen the logic I applied above before used in a batch file, and I'm wondering if there are some other side effects that I might not be considering. Is it possible that I will encounter unpredictable side effects down the road? Is this bad practice for any reason?
Update:
I hadn't posted my code, because I think it's hard to read, and it's a work in progress. Basically, what you're seeing here is argument parsing. I'm parsing the passed values in to categories - imagine this example: node --file=d.js. I'm calling the --file the param, and the d.js the arg. And much of the code below is creating an array of each.
#echo off
SETLOCAL enabledelayedexpansion
#SET "T=%1"
IF /i NOT DEFINED T (GOTO -h)
SET /a counter=1
SET /a argCount=0
SET /a index=0
for %%x in (%*) do set /A argCount+=1
for /l %%x in (1,2,%argCount%) do (
call SET param[!index!]=%%!counter!
set /a counter+=2
SET /a index+=1
)
SET /A paramCount=!index!
SET /a counter=2
SET /a index=0
for /l %%x in (2,2,%argCount%) do (
call SET arg[!index!]="%%!counter!"
set /a counter+=2
SET /a index+=1
)
for /l %%i in (0,1,!paramCount!) do (
SET "arg=!ARG[%%i]!"
SET "p=!param[%%i]!"
CALL :!p! !arg!
)
GOTO END
:-h
:--help
:--?
:-?
type %~dp0\lib\help.txt
GOTO END
:--unzip
SET "a=%1"
IF /i NOT DEFINED a (
SET "MASK=\d+-[a-z]+.zip"
) ELSE if [%a%]==["all"] (
SET "MASK=\d+-[a-z]+.zip"
) else (
SET "MASK=!a!"
)
for /f "usebackq tokens=*" %%i in (`dir /a /b %dls%^|grep -iE "!MASK!"`) do (
#7z x %dls%\%%i -omypath\share\icons
)
GOTO :EOF
:END
#echo Done
ENDLOCAL
Update: I don't know if this will be helpful for anyone, but the problem was with SET /A paramCount=!index!. That let to always looking for a parameter with an argument no matter what, so that if my second parameter didn't have an argument, or neither did, it was causing problems. I 'solved' the problem by setting paramCount to !index!-1, but I think the upshot of this is that it's quite difficult to pass arbitrary, potentially optional, parameters to batch files, and probably should be parsed differently than I have done - or using a different coding language. That said, it's working fine now for what I need.

Related

Batch File Random Syntax Errors

like I told in the question I get an unexplainable Syntax error from my Code.
I've bin searching for a program which automatically backups some saves every 5 Minutes. After I hadn't found anything which belongs to my purposes I decided to do it my own.. in Batch.
Here's the code:
#echo off
set name=Backup
for /f "tokens=1-3 delims=/:" %%a in ("%TIME%") do (set mytime=%%a.%%b,%%c)
set mytime=%mytime:~0,8%
set backupname=%name%_%date%_%mytime%
set dir1= (here comes the source directory)
set dir2= (here comes the target directory)
set countvar=1
:start
For /f "tokens=1-3 delims=/:" %%a in ("%TIME%") do (set mytime=%%a.%%b,%%c)
set mytime=%mytime:~0,8%
set backupname=%name%_%date%_%mytime%
set dir2=(here comes the target directory)\%backupname%
echo Backupordner: %backupname%
ROBOCOPY %dir1% %dir2%
if "%countvar%" == "1" (
set VarDir1=%dir2%
)
if "%countvar%" == "2" (
set VarDir2=%dir2%
)
if "%countvar%" == "3" (
set VarDir3=%dir2%
rmdir /S /Q "%VarDir1%"
set VarDir1=%VarDir2%
set VarDir2=%VarDir3%
set /a countvar=%countvar%-1
)
set /a countvar=%countvar%+1
#ping -n 30 localhost> nul
goto start
What it basically does is copying the files from the source directory into a folder, which is named after the date and time, in the target directory.
Caused by the high size of the backuped files I decided to add a feature, which deletes the third oldest save, so there are two remaining, newer save-files.
This is where the problem occurs: The first two "deletes" work properly, the third "delete" causes a syntax error. Everything runs normal after it.
Does anybody have an idea where the problem could be?
Yet another example of the delayedexpansion trap.
When vardir3 is established, it has no value, so vardir2 acquires nothing on the first occasion that count=3
On the second occasion, var1 acquires that value so on the third occasion, you get a syntax error as var1 is empty.
Solution: Forget var3 entirely. In count=3, set var2 to %dir2%.
Please search SO for the many, many articles on delayed expansion.
Also, you're better off using set "var=value" for a string assignment as it does not assign any trailing spaces that may be on the line.

Why I'm not being able to declare a variable inside a 'if exist' syntax in Batch (Windows) file?

In the following script few things won't work right and i'm having a hard time trying to figure the why, First it gives a 'Null' value to the variable (The .txt file is not empty), second it returns to me that some funcitons are not expected at that moment (Such as'goto' was not expected) so it maybe something with the syntax?
#echo off
set name1=sample1
set name2=sample2
set name3=sample3
set ccount=0
del mes.txt
:WFC
ping localhost -n 2 >nul
if exist mes.txt (
SetLocal EnableDelayedExpansion
set /p cname=<mes.txt
set /a ccount=%ccount%+1
if %cname%==!name%ccount%! goto AllowedList
if %ccount%==20 goto Crasher
goto WFC
I tryed calling echo %cname% in the end of the script and appareantly the var have a "nil" value, plus I also turned the #echo on and haven't found nothing useful to understand the problem.
The weirdest part is that if I take the variable declaration out of the 'if exist statemant', the whole script it works marvelously as in:
#echo off
set name1=sample1
set name2=sample2
set name3=sample3
set ccount=0
del mes.txt
:WFC
ping localhost -n 2 >nul
set /p cname=<mes.txt
if exist mes.txt (
SetLocal EnableDelayedExpansion
set /a ccount=%ccount%+1
if %cname%==!name%ccount%! goto AllowedList
if %ccount%==20 goto Crasher
goto WFC
So, this could be satisfying enough but it's not, there's a second software updating the mes.txt value all the time and I'm assuming that there will be some(not much, but some) cases in the second script where the var cname will get null value and still it will compare a null value with name1, completing excluding sample1 of the allowed lists. Can anyone solve this problem or at least explain the why?
Thankfully to the user MC ND, who awnsered me in the comments, I've managed to solve it, turns out that it was a really simple thing, his awnser was:
Your problem is delayed expansion (the lack of it) when reading %cname% and %ccount% (read here) – MC ND 24 mins ago
What I did to the script to make it work was:
#echo off
set name1=sample1
set name2=sample2
set name3=sample3
set ccount=0
del mes.txt
:WFC
ping localhost -n 2 >nul
set /p cname=<mes.txt
if exist mes.txt (
SetLocal EnableDelayedExpansion
set /a ccount=%ccount%+1
if !cname!==!name%ccount%! goto AllowedList
if !ccount!==20 goto Crasher
goto WFC
My guess is that it should be something more like this
:WFC
Timeout 1 1>Nul
If Not Exist mes.txt GoTo :WFC
SetLocal EnableDelayedExpansion
Set/P cname=<mes.txt
Set/A ccount+=1
If %ccount% Equ 20 GoTo :Crasher
If "!name%ccount%!" Equ "%cname%" GoTo :AllowedList
GoTo :WFC

Batch: compare files using CRC32

I have given myself a simple task of writing a batch file that will do the following:
Compare CRC32 of each file in "first" folder with each file in "second" folder. Output needs to be the name of every file from the "first" folder, which doesn't have its duplicate in "second".
Note: "CRC32.exe -nf" outputs CRC32 in first line, and file size in second one.
Here is how I tried to do it:
#echo off
for %%i in ("%cd%\first\*.*") do (
set uniq=0
for /f %%x in ('crc32 %%i -nf') do (
set one=%%x
goto find_two
)
:find_two
for %%j in ("%cd%\second\*.*") do (
for /f %%x in ('crc32 %%j -nf') do (
set two=%%x
goto compare
)
:compare
if one==two (
goto next
) else (
set uniq=1
)
)
if uniq==1 (
echo %%i >>result.txt
)
:next
)
I assume there are several errors present in this code, but I had trouble finding them. So if anyone has time, and thinks he can help, I would be grateful.
If you think different approach is required, feel free to show it.
There are two major problems.
Goto's inside brackt doesn't work as expected, they stop the loop immediately, so your code will simply fail there.
The solution is to avoid goto or to call a function and use the goto there.
The second problem are the compares.
if one==two ( will be always false, as it compare the word one against the word two, but not the content of the variables.
Normally you would use if "%one%"=="%two%" echo equal, but as it should work inside of brackets you need delayed expansion, so it looks like.
if !one!==!two! echo equal
Don't forget to enable the delayed expansion before you start the loop with
setlocal EnableDelayedExpansion
Might be easier to capture the output of crc32 using something like this:
for /f %%x in ('crc32 "%filename%" -nf ^| find /v "CRC32"') do stuff with %%x
Can you show me the full output of an example crc32 if this is not helpful?

Abnormal behavior of batch script in If else condition

As a beginner in batch file programming, i have created a batch file. Below is the code snippet-
SET INDEX=1
SET CURRJOBS=10
REM TOTALJOBS and CURRJOBS are dynamic but to keep code here, i have put static values to them
SET TOTALJOBS=1000
IF [%CURRJOBS%] LSS [%TOTALJOBS%] (
IF [%INDEX%] GEQ [5] (
SET /A INDEX=0
)
ECHO Started at %date% %time% with %CURRJOBS% jobs>>%CURRDIR%\JobSubmit.log
REM Here is a call to another bat file with Index.
ECHO Finished at %date% %time% with %CURRJOBS% jobs>>%CURRDIR%\JobSubmit.log
SET /A INDEX+=1
GOTO START
)ELSE (
ECHO Finished at %date% %time% with %CURRJOBS% jobs>>%CURRDIR%\JobSubmit.log
)
Now, this code, sometimes work, sometimes not.
however there is some syntax error which might be a cause to behave abnormally. Is there any IDE or online utility to check the syntax of batch file?
What is wrong with above code?
Comparisons in IF command are of two types: string or number. To indicate IF that we want number comparison, the numbers must be written with no additional characters. So, your code should be written this way:
IF %CURRJOBS% LSS %TOTALJOBS% (
IF %INDEX% GEQ 5 (
SET /A INDEX=0
)
When a variable or parameter may have an empty value, it is customary to enclose it between quotes to avoid syntax errors, for example:
IF "%POSSIBLEEMPTYVAR%" NEQ "" (
If the variable have string values, you may use the same format for both check for empty value and do the comparison:
IF "%VARIABLE%" equ "THIS VALUE" GOTO OK
However, if a variable may be empty and you want to compare it as number, both tests must be made.

Batch script: local variable from function1 to function2

Okay guys, let my try to explain my problem:
I start with a line from where I start 2 different functions
setlocal EnableDelayedExpansion
for %%i in ("C:\*.*") do (
call :function1 "%%~i"
call :function2 "%%~i"
)
goto :eof
In function1, at a certain point I DO a SET in a local environment:
setlocal EnableDelayedExpansion
...
...
set name1=blabla
endlocal & SET name=%name1%
echo %name%
goto :eof
The echo does return my variable. Now onto my problem.
I quit function one and i go to function 2 (see first code-segment)
I can't call the variable form here. I tried in the function2, I tried before function2 is called, but both didn't resolve the issue.
My guess is a have set only a local variable for function1. I search the nets but i read that the line "endlocal & SET name=%name1%" should have solved my issue.
I hope I have explained it well, all help appreciated!
I'm not sure where your problem lies since this works perfectly:
#setlocal enableextensions enabledelayedexpansion
#echo off
set name=ORIGNAME
for %%i in (1 2) do (
call :function1 %%i
echo in main %name% !name!
)
endlocal
goto :eof
:function1:
setlocal enableextensions enabledelayedexpansion
set name1=%1_blabla
endlocal & SET name=%name1%
echo in function %name%
goto :eof
outputting:
in function 1_blabla
in main ORIGNAME 1_blabla
in function 2_blabla
in main ORIGNAME 2_blabla
Are you certain that, when you used name in the main areas, you used !name! instead of %name%?
If you used the %name% variant, that would be evaluated when the entire for loop was read, not at the time when you used it (in other words, it would be blank). You can see that in the output of ORIGNAME in the main line.
I'm not certain that's the case since you are using delayed expansion. But, just in case, I thought I'd mention it. I always use delayed expansion and I always used the ! variants of the environment variables since it more closely matches how I expect a shell to work.
In any case, the code I've given works fine so you may want to fiddle with that to see if you can incorporate it into your own.
First a small working sample
#echo off
setlocal EnableDelayedExpansion
call :myAdd returnVar 1 2
echo 1. Percent %returnVar%
echo 1. Exlcam !returnVar!
(
call :myAdd returnVar 4 5
echo 2. Percent %returnVar%
echo 2. Exlcam !returnVar!
)
goto :eof
:myAdd
setlocal
set /a result=%2 + %3
(
endlocal
set %1=%result%
goto :eof
)
---- Output ----
1. Percent 3
1. Exlcam 3
2. Percent 3
2. Exlcam 9
The cause for the wrong result in "2. Percent" is a result of the expanding of %var%.
They are expanded at the time of parsing, before executing any line of the parenthesis block, so there is the old value in returnVar.
But that is also the cause why the returning of variables from a function works.
the endlocal removes all local variables, but the block (an ampersand works the same way)
is expanded before the "endlocal" is executed.
It's a good idea to test this issues with "echo on".

Resources