"( was unexpected at this time" with if statement - if-statement

I am new to batch and making my first batch project with custom commands using doskey, mainly for myself. I have been successful with all of my scripts in my project up until this point. I have a command that tells the user all the commands (5 so far), much like the help command, except for some reason I am getting errors (described in the title). I might have an idea where the error is coming from, but I have no idea how to fix it. I have checked multiple sources but none of them are even close to what I need to fix, so here is my code:
#echo off
IF "%~1" == "" (
echo For more information on a specific command, type CMDS command-name
echo ADD Adds specified NUM amount to variable
echo NUM Sets the NUM amount to a specified value
echo RESET Sets variable to 0
echo SETNUM Sets variable to a specified value
echo SUB Subtracts specified NUM amount from variable
)
IF /I %1 == ADD (
echo The ADD command will add the amount specified from the NUM command to a variable specified as num
)
IF /I %1 == NUM (
echo The NUM command will set the variable specified as num to a specific value
)
IF /I %1 == RESET (
echo The RESET command will reset the variable specified as num to 0
)
IF /I %1 == SETNUM (
echo The SETNUM command will set the amount that gets added to/subtracted from the variable specified as num to a specific value
)
IF /I %1 == SUB (
echo The SUB command will subtract the amount specified from the NUM command from a variable specified as num
)
IF not /I %1 == ADD (
IF not /I %1 == NUM (
IF not /I %1 == RESET (
IF not /I %1 == SETNUM (
IF not /I %1 == SUB (
echo %1 is not a command! //I think the error is coming from one of the if statements in these nested ifs, because this is the only thing that doesn't work
)
)
)
)
)
#echo on
Any help would be appreciated.

Batch is a really, really broken "programming language".
If you do not provide an argument, consider what happens here:
IF /I %1 == ADD (
echo The ADD command will add the amount specified from the NUM command to a variable specified as num
)
The %1 is replaced with nothing. Which results in:
IF /I == ADD (
echo The ADD command will add the amount specified from the NUM command to a variable specified as num
)
The solution is to always use "%~1" == "something".
And as Compo pointed out, it's IF /I NOT.
Maybe don't compulsively put #echo off in your file if you're trying to debug it... It would have shown what the problem is.

Related

Capitalize first letter of the variable in the windows batch file

I'm trying to capitalize the first letter of the output from the %COMPUTERNAME% variable. I have tried the below code which doesn't work.
set host=%COMPUTERNAME%
echo %host% | sed 's/^\(.\)/\1\u\2/g/'
The output should be Beast rather than BEAST or beast
I ever often tend to wrap a powershell command for this:
:: Q:\Test\2018\10\12\SO_52769852.cmd
#Echo off
For /f %%A in ('
Powershell -NoP -C "$Env:COMPUTERNAME.Substring(0,1).ToUpper()+$Env:COMPUTERNAME.Substring(1).ToLower()"
') do set host=%%A
Echo:%host%
Instead of using `SEd`, you could probably do it as a single line using the built-in `Find` command.
#For /F "Tokens=2 Delims=:" %%A In ('"Find "" ":%ComputerName:~,1%" 2>&1"') Do #Echo %%A%ComputerName:~1%
The idea uses a 'quirk' with find.exe, which capitalizes the entire filename in its error message, when it cannot locate a file. I expand the %COMPUTERNAME% variable, asking for just its first character, %ComputerName:~,1%,and precede that with a character which is invalid in a Windows filename, in this case :. If we assume a %COMPUTERNAME% value of iab-desktop, the error message, (stdOut, 2>) from Find "" ":i" would be passed to the Do portion as, File not found - :I. This is the English version string, but that shouldn't matter, because we have asked for the second token delimited by the : character, which will be I. I then prepend that result, stored in %%A to the expanded value of %COMPUTERNAME%, this time asking for all characters except for its first, %ComputerName:~1%. The resulting string will be the value of %COMPTERNAME% with the first character capitalized.
try this:
#echo off
setlocal
set "f_leter=%COMPUTERNAME:~0,1%"
set "the_rest=%COMPUTERNAME:~1%"
call :UpCase %f_leter% f
call ::LoCase %the_rest% rest
set result=%f%%rest%
echo %result%
exit /b %errorlevel%
endlocal
::http://www.robvanderwoude.com/battech_convertcase.php
:LoCase
:: Subroutine to convert a variable VALUE to all lower case.
:: The argument for this subroutine is the variable NAME.
setlocal enableDelayedExpansion
set "var=%~1"
FOR %%i IN ("A=a" "B=b" "C=c" "D=d" "E=e" "F=f" "G=g" "H=h" "I=i" "J=j" "K=k" "L=l" "M=m" "N=n" "O=o" "P=p" "Q=q" "R=r" "S=s" "T=t" "U=u" "V=v" "W=w" "X=x" "Y=y" "Z=z") DO (
SET "var=!var:%%~i!"
)
endlocal&(
if "%~2" neq "" (
set "%~2=%var%"
) else (
echo %var%
)
)&GOTO:EOF
:UpCase
setlocal enableDelayedExpansion
set "var=%~1"
:: Subroutine to convert a variable VALUE to all UPPER CASE.
:: The argument for this subroutine is the variable NAME.
FOR %%i IN ("a=A" "b=B" "c=C" "d=D" "e=E" "f=F" "g=G" "h=H" "i=I" "j=J" "k=K" "l=L" "m=M" "n=N" "o=O" "p=P" "q=Q" "r=R" "s=S" "t=T" "u=U" "v=V" "w=W" "x=X" "y=Y" "z=Z") DO (
SET "var=!var:%%~i!"
)
endlocal&(
if "%~2" neq "" (
set "%~2=%var%"
) else (
echo %var%
)
)&GOTO:EOF
Here's the sed answer, although I'd recommend #LotPings powershell answer on Windows. Note that for a typical %computername% the more key point of your question is converting all but the first character to lower-case.
set host=%COMPUTERNAME%
echo %host% | sed -r 's/^(.)(.*)/\U\1\L\2/'

Batch file: evaluating command output with regex?

I have made a Windows batch file based on some examples I have found elsewhere.
What it does is it parses a folder for a specific file type (.mkv) and then runs mkvmerge.exe from the MKVToolNix folder.
The command produces an output, listing the different tracks in the container.
The core of the file is
set rootfolder="Z:\Movies"
for /r %rootfolder% %%a in (*.mkv) do (
for /f %%b in ('mkvmerge -i "%%a" ^| find /c /i "chapters"') do (
if [%%b]==[0] (
echo "%%a" has no chapters
) else (
echo Doing some interesting stuff!
)
)
)
The above example is just part of the file, rootfolder is set to the folder I want parsed, of course, and upon finding a file with chapters in it, it will run additional commands.
It all works beautifully but I also want to check for subtitles at the same time. The find command doesn't take regular expressions or I could just have added "chapters subtitles". My efforts using other commands, like findstr, haven't really worked.
How do I go about using RegEx here?
This is an example output, running mkvmerge.exe on an .mkv file
mkvmerge.exe -i "Snow White and the Seven Dwarfs (1937) [tt0029583].mkv"
File 'Snow White and the Seven Dwarfs (1937) [tt0029583].mkv': container: Matroska
Track ID 0: video (MPEG-4p10/AVC/h.264)
Track ID 1: audio (DTS)
Track ID 2: audio (AC-3)
Track ID 3: subtitles (HDMV PGS)
Track ID 4: subtitles (HDMV PGS)
Chapters: 26 entries
This example has both subtitle and chapter tracks and the batch file will find the keyword "chapters" (it's set to ignore case). I also want to catch the files that contain the keyword "subtitles" even when there are no chapters.
To clarify my intent here, I want the code to:
Parse through the given folder
For all .mkv files, do mkvmerge /i which will output (as text) the streams in that file
Look at that output and if it contains the word(s) "chapters" and/or "subtitles" trigger some action.
Since you appear to make no distinction between whether one string or the other (or both) is detected, then
#Echo off
set rootfolder="Z:\Movies"
for /r %rootfolder% %%a in (*.mkv) do (
mkvmerge -i "%%a" | findstr /i "^Chapters subtitles" >nul
if errorlevel 1 (
echo Neither Chapters nor subs found in "%%a"
) else (
echo Chapters or subs found in "%%a"
)
)
would likely be easier.
Thanks LotPings for your effort. I learned something about tokens that will be very useful. Your script also got me on the right track (learning a few more commands on the way.
Your script ended up looking like this:
:: Q:\Test\2018\05\27\SO_50555308.cmd
#Echo off
set rootfolder="Z:\Movies"
for /r %rootfolder% %%a in (*.mkv) do (
set "found="
for /f "tokens=1-4* delims=: " %%b in (
'mkvmerge -i "%%a" ^| findstr /i "^Chapters subtitles"'
) do (
if /i "%%b"=="Chapters" set found=1
if /i "%%e"=="subtitles" set found=1
)
if defined found (
echo Chapters or subs found in "%%a"
) else (
echo.
)
)
All I needed was to check whether one of my keywords were present in any of the tokens and then set a flag accordingly and after the loop do the appropriate action, resetting the flag for the next file.
Still unclear how a line with Subtitles would look like.
Presuming the values Chapters and Subtitles appear in 1st column
The for /f splits the lines at colon and space (adjacent delims count as one) into tokens 1=%%b and 2=%%c the possible unsplitted rest in token 3=%%d
The space separated (ORed) RegEx search words anchored ^ at line begin will only match chapters/subtitles.
:: Q:\Test\2018\05\27\SO_50555308.cmd
#Echo off
set rootfolder="Z:\Movies"
for /r %rootfolder% %%a in (*.mkv) do (
for /f "tokens=1-4* delims=: " %%b in (
'mkvmerge -i "%%a" ^| findstr /i "^Chapters subtitles"'
) do (
if /i "%%b"=="Chapters" if "%%c"=="0" (
echo "%%a" has no chapters
) else (
echo "%%a" has %%c chapters
echo Doing some interesting stuff!
)
if /i "%%e"=="subtitles" echo "%%a" %%b %%b %%d: %%e %%f
)
)

Use regexp in .BAT to find substring

How can I check contains of substring in string variable of batch file?
for example:
set var1=foobarfoo
set regexp=.*bar.*
if [check var1 by regexp] echo "YES"
In my case, it's must be only check by regular expression and only in .bat file.
Adjusting the answer found here give
#echo off
set var1=foobarfoo
set "regexp=.*bar.*"
echo %var1%
echo %regexp%
setlocal enableDelayedExpansion
echo(%var1%|findstr /r /c:"%regexp%" >nul && (
echo FOUND
rem any commands can go here
) || (
echo NOT FOUND
rem any commands can go here
)

batch file: if %variable% (commands)

i want to use my %variable% to manage the conditional clauses in a IF.. THEN.. ELSE in a batch file.
Something like the following:
set variable=%%homedrive%% EQU C:
if %variable% (
echo test ok
) else (
echo test fail
)
if i write on a cmd console:
set test=1 equ 1
if %test% echo OK
it works!
i'll use it in a for /f cicle:
this is my pseudo codethis is my pseudo code to correct
(
rem echo "%systemdrive%;;"
echo "%%COMPUTERNAME%% EQU [x];[some parameters1]"
echo "%%USERNAME%% NEQ [y];[some parameters2]"
echo "%%LOGONSERVER%% EQU [z];[some parameters3]"
[..]
) > "%temp%\CSG_fs.tmp"
[..]
for /f "usebackq tokens=1-2* delims=;" %%a in ("%temp%\CSG_fs.tmp") do (
set cond=%%a& set cond=!cond:~1!
set parm=%%b& set parm=!parm:~0,-1!
echo - cicle: "!cond!" --^> "!parm!"
call if !cond! call:CSG_sub_fs !parm!
echo - done
)
goto:eof
:CSG_sub_fs
[..]
goto:eof
--edit--
how can i use the variable !cond! to decide if execute the call to CSG_sub_fs?
call if !cond! call:CSG_sub_fs !parm!
does not work because it returns: "Can not find the batch label specified - IF"
and if i use
if !cond! call:CSG_sub_fs !parm!
it will say: "call:CSG_sub_fs not expected"
Well - there doesn't seem to be a question, so it's not that easy to answer.
You have a problem with
echo "^%COMPUTERNAME^% EQU [x];[some parameters1]"
because ^ does not escape % - % escapes % - use %%COMPUTERNAME%%...
(you should have been able to check this just by TYPEing "%temp%\CSG_fs.tmp"
Next problem is that
for /f "tokens=1-2* delims=; usebackq" %%a in (%temp%\CSG_fs.tmp) do (
may process the file %temp%\CSG_fs.tmp provided %temp%\CSG_fs.tmp contains no spaces, semicolons or commas. If it contains any of these deafult separators, or certain other characters with a special meaning, then you must enclose the filename in double-quotes "%temp%\CSG_fs.tmp"and use the usebackq option.
You've attempted to use usebackq but DELIMS must be the LAST option if it is used. Your code would set ";","","u","s","e","b","a","c","k" and "q" as delimiters.
Beyond that, perhaps if you explain what you intend to achieve, we'd be able to devise the appropriate code.
Try this:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
CLS
REM I'm setting these variables for testing.
REM That isn't ususally a good idea but the SETLOCAL
REM will ensure they are restored on exit
SET computername=[x]
SET logonserver=[z]
(
rem echo "%systemdrive%;;"
echo "%%COMPUTERNAME%% EQU [x];[some parameters1]"
echo "%%USERNAME%% NEQ [y];[some parameters2]"
echo "%%LOGONSERVER%% EQU [z];[some parameters3]"
) > "%temp%\CSG_fs.tmp"
for /f "usebackqtokens=1-2* delims=;" %%a in ("%temp%\CSG_fs.tmp") do (
set cond=%%a& set "cond=IF !cond:~1! CALL :csg_sub_fs "
set parm=%%b& set parm=!parm:~0,-1!
CALL :varcmd "!cond!" "!parm!"
)
GOTO :eof
:varcmd
%~1 %~2
GOTO :eof
:csg_sub_fs
ECHO parameters supplied to csg_sub_fs were: %*
GOTO :eof
I've forced the variablenames to match the conditions you've used in order to trigger the subroutine calls. Change as you need to prove your concept.
And dont worry about imperfect English. I'm sure I wouldn't do as well in your language!

Regex to identify DOS environment variables within braces

The following DOS script snippet has a bug:
if not exist %MyFolder% (
mkdir %MyFolder%
if %errorlevel% GEQ 1 (
rem WARNING: the line above has a bug!
rem %errorlevel% will be the errorlevel
rem of the if statement because of the (parentheses)
echo Error: Could not create folder %MyFolder%
goto AnErrorOccurred
)
)
The fix is to use setlocal enabledelayedexpansion as follows:
setlocal enabledelayedexpansion
if not exist %MyFolder% (
mkdir %MyFolder%
if !errorlevel! GEQ 1 (
rem WARNING: the line above has a bug!
rem !errorlevel! will be the errorlevel
rem of the if statement because of the (parentheses)
echo Error: Could not create folder %MyFolder%
endlocal & goto AnErrorOccurred
)
)
endlocal
A full explanation of why is available here: Batch file fails to set environment variable within conditional statement
I want to audit my code to find instances of this bug, I figure a Regex would be an appropriate match, but haven't managed to get one working...
I think the ingredients should be:
Match an environment variable surrounded with %percentsigns%
That is inside (parentheses)
Any suggestions?
grepWin
I would use grepWin. Depending on the number of instances you have to find, you could write a regex that will give you all of them, plus some false positives.
Example: bug.bat
if not exist %MyVar% echo Hi!
if not exist %MyFolder% (
mkdir %MyFolder%
if %errorlevel% GEQ 1 (
rem WARNING: the line above has a bug!
rem %errorlevel% will be the errorlevel
rem of the if statement because of the brackets
echo Error: Could not create folder %MyFolder%
goto AnErrorOccurred
)
)
Then use a regular expression to match all lines that start with if and have an open parenthesis:
$ grep "^[ \t]*if.*(" bug.bat
if not exist %MyFolder% (
if %errorlevel% GEQ 1 (
grepWin will show you all the files that match.
Percent Symbols
By request:
grep "^[ \t]*if.*%.*%.*(" bug.bat
You can't solve it with Regex, because you can't count the parenthesis with a regular language. For example:
Stuff (
More Stuff (
Less Stuff )
A %var%
Less Stuff )
There is no ( between the last ) and the variable. Since I can't count how many `( appear before to know if there's one open, I can't do this with regular expressions.
As you can see, he is using ! rather than % even in the rootlevel of the batch.
So basically you should be able to alternate every % to an ! for your environmental variables.
One other way is to use the AND (&&) and OR (||) tests for success (errorlevel 0) or error (errorlevel > 0), as in:
if not exist %MyFolder% mkdir %MyFolder%|| (
echo Error: Could not create folder %MyFolder%
goto AnErrorOccurred
)
I hope this can help.
And by the way, you do not fail to set the variable, you fail to read it back.
What happen is, within parenthesis, using %%'s give you the states which was before you entered the said parenthesis. The only thing I knows work well is the math "set /a toto+=1" which will correctly increment the variable. Otherwise you have two options:
Either use a called function to set the variable or use the setlocal ENABLEDELAYEDEXPANSION statement, as previously stated, and use !!'s within the parenthesis.