windows batch syntax error in a not used if block - windows

why the following batch script is failing with "The syntax of the command is incorrect." error on Windows 7 if I provide no argument:
IF NOT [%1]==[] (
echo "blablabla" > %1
) ELSE (
echo "please provide argument"
)
But there is no problem with this code:
IF NOT [%1]==[] (
echo "blablabla"
) ELSE (
echo "printing not existing argument: %1"
)
Thanks in advance

Encapsulating the filename by double quotations enforces the if/else block to parse and read the expression correctly.
For example
IF NOT [%1]==[] (
echo "blablabla" > "%1"
) ELSE (
echo "please provide argument"
)

The problem is that the entire if/else block is read and parsed at once. So if %1 is empty, the redirection > is still parsed and evaluated invalid.
You can work around the issue like that:
if not "%~1"=="" (
goto :Redirect
) else (
echo please provide argument
)
goto :Continue
:Redirect
> "%~1" echo blablabla
:Continue
I replaced the brackets by quotation marks, because they protect white-spaces and other special characters. The ~-symbol ensures that the returned argument appears unquoted, so there is always one pair of quotes in the expression "%~1".
Moreover, I removed quotes from the echo command lines as they were output too, and I moved the redirection part > "%~1" in front of echo in order to avoid a trailing SPACE to be returned.

Related

Call batch file with variable number of arguments

Basically, the script below can be used in three different ways:
call foo
.bat boot_up "path"
.bat halt "path"
.bat ssh "path" "command"
I cannot guarantee that the path or the command does not have spaces.
When I use foo.bat for executing my subroutine ssh, then everything works. Instead, when I try to call my subroutines boot_up or halt, an error apprears:
( was unexpected at this time.
But, if I add a third argument to boot_up or halt, then everything works again.
So my question is, how do I manage call of batch files with variable lenght of arguments?
:main
echo Argument 1: (%1)
echo Argument 2: (%2)
echo Argument 3: (%3)
call :set_cygwin_env || exit /b 1
if not "%1"=="" if not %2=="" (
if "%1"=="boot_up" (
call :boot_up %2
) else if "%1"=="halt" (
call :halt %2
) else if "%1"=="ssh" if not %3=="" (
call :ssh %2 %3
) else (
call :show_help || exit /b 1
)
) else (
call :show_help || exit /b 1
)
:exit
The source of your error is ) else if "%1"=="ssh" if not %3=="" ( - When you don't pass a 3rd argument, your code expands to ) else if "halt"=="ssh" if not =="" (, which is invalid syntax. The entire compound statement must have valid syntax, even the branches that don't fire. You must make sure the left side of the comparison has at least one character. Enclosing quotes are typically used, because they protect against poison characters like & and |, and token delimiters like space, comma, equal, tab.
In general, you should use if "%~1"=="someValue" ... when doing comparisons with command line arguments. The ~ removes any existing enclosing quotes, and then you explicitly add your own. It is important to first remove the quotes because you cannot anticipate whether the user added their own quotes or not. A value like "this&that" could be passed in, so "%1" would expand to ""this&that"", and the & would no longer be quoted. "%~1" expands to the desired "this&that". This strategy is not fool proof, but it is about as good as it gets without doing crazy batch programming that you probably don't want to get into.
So your fixed code should look like
:main
echo Argument 1: (%1)
echo Argument 2: (%2)
echo Argument 3: (%3)
call :set_cygwin_env || exit /b 1
if not "%~1"=="" if not "%~2"=="" (
if "%~1"=="boot_up" (
call :boot_up %2
) else if "%~1"=="halt" (
call :halt %2
) else if "%~1"=="ssh" if not "%~3"=="" (
call :ssh %2 %3
) else (
call :show_help || exit /b 1
)
) else (
call :show_help || exit /b 1
)
:exit
you could pre-process your arguments to remove passed quotes as follows (I simplified your code to create an independent test)
#echo off
:main
set ARG1=%1
set ARG2=%2
set ARG3=%3
if x%ARG1%==x goto skarg1
if ^%ARG1:~0,1%==^" set ARG1=%ARG1:~1,-1%
:skarg1
if x%ARG2%==x goto skarg2
if ^%ARG2:~0,1%==^" set ARG2=%ARG2:~1,-1%
:skarg2
if x%ARG3%==x goto skarg3
if ^%ARG3:~0,1%==^" set ARG3=%ARG3:~1,-1%
:skarg3
echo Argument 1: (%ARG1%)
echo Argument 2: (%ARG2%)
echo Argument 3: (%ARG3%)
if "%ARG1%"=="boot_up" if not "%ARG2%"=="" (
echo bootup "%ARG2%"
)
:exit
if argument is empty, just leave it as-is
if argument starts with quotes, just remove the first & last char
use %ARGx% (quoted) from now on instead of %1,%2
quick test using only argument 2:
L:\>args boot_up
Argument 1: (boot_up)
Argument 2: ()
Argument 3: ()
L:\>args boot_up arg2
Argument 1: (boot_up)
Argument 2: (arg2)
Argument 3: ()
bootup "arg2"
L:\>args boot_up "arg2 space"
Argument 1: (boot_up)
Argument 2: (arg2 space)
Argument 3: ()
bootup "arg2 space"
L:\>args boot_up "arg2"
Argument 1: (boot_up)
Argument 2: (arg2)
Argument 3: ()
bootup "arg2"

Parsing optional arguments with quotes and spaces

I want to pass optional parameters to the script like that:
mybatfile.bat firstParameter -r 000 -m "Some message here"
I've got solution from different question, but I have problem with -m "Some message here"
SET firstParameter=%1
SET message=""
SET roomNumber=""
SHIFT
:loop
IF NOT "%1"=="" (
IF "%1"=="-r" (
SET roomNumber=%2
)
IF "%1"=="-m" (
set message=%~2
)
SHIFT
GOTO :loop
)
%~2 only gets first part of the word because it see every word as another parameter.
I also find out I could use small hack inside if, like that
for %%x in (%*) do (
echo "%%~x"
)
And here take first "%%~x" after "-m" appear. But how to do that using script?
You are misdiagnosing your problem. The quotes around the message parameter will preserve the space literals.
But your test for no parameter is wrong, and your are missing some SHIFT commands.
Below is a minimal correction of your code.
#echo off
setlocal
set firstParameter=%1
set "message="
set "roomNumber="
:loop
shift
if not "%~1"=="" (
if "%~1"=="-r" (
set roomNumber=%2
shift
)
if "%~1"=="-m" (
set message=%2
shift
)
goto :loop
)
echo firstParameter = %firstparameter%
echo roomNumber = %roomNumber%
echo message = %message%
Sample usage:
C:>mybatfile firstParameter -r 000 -m "Some message here"
firstParameter = firstParameter
roomNumber = 000
message = "Some message here"
You really should consider my argument parsing template at https://stackoverflow.com/a/8162578/1012053. Most of that code remains constant, no matter how many optional arguments you define. It supports default values for unused parameters, and is very robust.

Nested IF Statement, unexpected error

I have created a nested if statement that just performs very simple tasks. Here is the code below:
#ECHO OFF
SET ANS=%1
IF "%ANS%"=="" ( ECHO You Entered Nothing
)
IF /i %ANS%==Y ( ECHO You entered Yes
)
IF /i %ANS%==N ( ECHO You entered NO
)
IF %ANS%==? ( ECHO I am confused
)
My problem is that when "%ANS%"=="" i get a "( was unexpected at this time" after echoing the message i have provided. Everything else works as planned but i am not sure why i am getting this message.
The error message comes from your second IF.
If %1 is empty, your first IF gives you "You entered nothing" as intended.
Then the second IFline translates to
IF /i ==Y ( ECHO You entered YES
(because %ANS% is empty)
Therefore you get a "( was unexpected at this time).
To correct this, write
IF /i "%ANS%"=="Y" ( ECHO You entered Yes
This has elements that protect it from spaces and duoble quotes and & characters.
#ECHO OFF
SET "ANS=%~1"
IF "%ANS%"=="" ( ECHO You Entered Nothing )
IF /i "%ANS%"=="Y" ( ECHO You entered Yes )
IF /i "%ANS%"=="N" ( ECHO You entered NO )
IF "%ANS%"=="?" ( ECHO I am confused )
Try this one ... it works perfectly
#ECHO OFF
SET ANS="%1"
IF %ANS%=="" ( ECHO You Entered Nothing
)
IF /i %ANS%=="Y" ( ECHO You entered Yes
)
IF /i %ANS%=="N" ( ECHO You entered NO
)
IF %ANS%=="?" ( ECHO I am confused
)

How can I check if a variable contains another variable within a windows batch file?

Assuming the following batch file
set variable1=this is variable1
set variable2=is
set variable3=test
if variable1 contains variable2 (
echo YES
) else (
echo NO
)
if variable1 contains variable3 (
echo YES
) else (
echo NO
)
I want the output to be a YES followed by a NO
I've resolved this with the following
setLocal EnableDelayedExpansion
set variable1=this is variable1
set variable2=is
set variable3=test
if not "x!variable1:%variable2%=!"=="x%variable1%" (
echo YES
) else (
echo NO
)
if not "x!variable1:%variable3%=!"=="x%variable1%" (
echo YES
) else (
echo NO
)
endlocal
I got the basic idea from the following answer but it wasn't searching by a variable so it wasn't completely what I was looking for.
Batch file: Find if substring is in string (not in a file)
another way:
echo/%variable1%|find "%variable2%" >nul
if %errorlevel% == 0 (echo yes) else (echo no)
the / prevents output of Echo is ON or Echo is OFF in case %variable1% is empty.
Gary Brunton's answer did not work for me.
If you try with set variable1="C:\Users\My Name\", you will end up with an error :
'Name\""' is not recognized as an internal or external command
Adapting this answer Find out whether an environment variable contains a substring, I ended up with :
echo.%variable1%|findstr /C:"%variable2%" >nul 2>&1
if not errorlevel 1 (
echo Found
) else (
echo Not found
)
The following is based on JBEs answer in this thread. It distinguishes empty/undefined variables.
if not defined VARIABLE1 (echo VARIABLE1 undefined) & goto proceed
if not defined VARIABLE2 (echo VARIABLE2 undefined) & goto proceed
echo %VARIABLE1% | find "%VARIABLE2%" > nul
if ERRORLEVEL 1 (echo Not found) & goto proceed
echo Found
:proceed
If the value of VARIABLE1 contains parentheses, e.g. the value C:\Program Files (x86), then the parentheses may be interpreted as a distinct command rather than echoed, causing an error. Substrings like (x86) can be escaped with carets for purposes of echoing properly, e.g.:
SET V1_DISPLAY=%VARIABLE1%
if /i "%V1_DISPLAY:(x86)=%" NEQ "%V1_DISPLAY%" set V1_DISPLAY=%V1_DISPLAY:(x86)=^^(x86^^)%
echo %V1_DISPLAY% | find "%VARIABLE2%" > nul
The second statement, above, can be read as follows:
If replacing substring (x86) with nothing makes a difference, then (x86) is present, so replace any occurrence of (x86) with ^^(x86^^), where each pair of carets represent an escaped single caret.

Echo of String with Double Quotes to Output file using Windows Batch

I'm attempting to rewrite a configuration file using a Windows Batch file.
I'm looping through the lines of the file and looking for the line that I want to replace with a specified new line.
I have a 'function' that writes the line to the file
:AddText %1 %2
set Text=%~1%
set NewLine=%~2%
echo "%Text%" | findstr /C:"%markerstr%" 1>nul
if errorlevel 1 (
if not "%Text%" == "" (
setlocal EnableDelayedExpansion
(
echo !Text!
) >> outfile.txt
) else (
echo. >> outfile.txt
)
) else (
set NewLine=%NewLine"=%
setlocal EnableDelayedExpansion
(
echo !NewLine!
) >> outfile.txt
)
exit /b
The problem is when %Text% is a string with embedded double quotes.
Then it fails. Possibly there are other characters that would cause it to fail too.
How can I get this to be able to work with all text found in the configuration file?
Try replacing all " in Text with ^".
^ is escape character so the the " will be treated as regular character
you can try the following:
:AddText %1 %2
set _Text=%~1%
set Text=%_Text:"=^^^"%
... rest of your code
REM for example if %1 is "blah"blah"blah"
REM _Text will be blah"blah"blah
REM Text will be blah^"blah^"blah
Other characters that could cause you errors (you can solve it with the above solution) are:
\ & | > < ^
In a windows batch shell (command) double quote are escape with ^ on standard command line BUT with a double double quote inside a double quoted string
echo Hello ^"Boy^"
echo "Hello ""Boy"""
(remark: second line will produce the external surrounding double quote in the output also)

Resources