Why empty variables cause crash - windows

Using empty variables doesn't necessarily cause a crash. But when using them inside an IF case will crash the program. Why? Even if EnableDelayedExpansion is not set here, why isn't e.g. the variable PATHDEF below not treated as an empty variable?
Calling the below script with input APP will cause a crash with different error messages. (I.e I call the script below with an input argument which will lead to IF evaluating to true)
On windows console: \Intel\iCLS was unexpected at this time.
On Bamboo server: \Graphviz2.38\bin was unexpected at this time.
echo off
SET app=%1
SET PATHDEF=%PATH%
echo %PATHDEF% <--- This works. Prints path
echo %THIS% <--- This works. Prints "ECHO is off" (since %THIS% is empty)
IF %app%==APP (
echo %PATHDEF% <--- This causes crash!
)

This is happening because of the unescaped close parenthesis in %PATH%. The first item in your PATH variable is C:\Program Files (x86)\Intel\iCLS Client\.
Variables are expanded at runtime, so
IF %app%==APP (
echo %PATHDEF%
)
is actually being read as
IF %app%==APP (
echo C:\Program Files (x86)\Intel\iCLS Client\;C:\WINDOWS\system32;C:\WINDOWS;
)
Because of how the cmd interpreter parses parentheses, it decides that the first unquoted, unescaped close parentheses is the end of the code block, so your code is being treated like you wrote
IF %app%==APP (
echo C:\Program Files (x86
)
\Intel\iCLS Client\;C:\WINDOWS\system32;C:\WINDOWS;
And since things outside of code blocks get processed first, the script recognizes that \Intel\iCLS is not a valid command, so it throws an error there instead of echoing C:\Program Files (x86.
There are two ways to avoid this:
OPTION ONE - Put the variable in quotes
IF %app%==APP (
echo "%PATHDEF%"
)
Unfortunately, this means the quotes get included when being displayed.
OPTION TWO - Put everything on the same line
IF %app%==APP echo %PATHDEF%
With no opening parenthesis to mess everything up, the contents will print correctly without the need to quote or escape anything.

Related

Batch script interpreting content inside comment?

When I run the following batch script:
#echo off
REM %~ will strip surrounding quotes if any
echo HERE
I get the following error:
C:\>test.cmd
The following usage of the path operator in batch-parameter
substitution is invalid: %~ will strip surrounding quotes if any
For valid formats type CALL /? or FOR /?
Same effect if REM is changed to ::.
Seems like the parser is ignoring the comment indicator and parsing the %~. If I put a space between the % and ~ then it works fine.
Windows 7 Enterprise (have not checked any other versions).
Seems like a bug to me, but am I missing something?
The %-expansion, hence expanding normal environment variables (like %VAR%) as well as command line arguments (like %0), is the very first step after having read a line, therefore it happens even before the rem command is recognised. Thus you need to avoid the %~ (by writing rem % + ~ ..., for instance).
Given that the command extensions are enabled, which is the default anyway, %~ is recognised as invalid argument syntax (the ~ is expected to be followed by a decimal digit denoting the argument position or by a valid modifier like f, d, p, n, x, etc.; see Command Line arguments (Parameters)) and results in a fatal error, meaning that an error message is thrown and batch file processing is aborted (the %ErrorLevel% is not set though).
The same effect comes up when you try to do sub-string substitution but specifying an empty search string (like %VAR:=replace% or %VAR:*=replace%, given that VAR is defined), also with command extensions enabled.
See also this thread: How does the Windows Command Interpreter (CMD.EXE) parse scripts?
I think it is clearly covered in quite a few docs that cmd will interpret the arguments before comments, see the example in #LotPings comment as well as #aschiphl's post. That being said, you can momentarily disableextensions and then turn it back on when needed. The below example shows how disabling it will allow you to use it in the REM comment and then enabled again after to show allow extensions:
#echo off
setlocal disableextensions
REM %~ will strip surrounding quotes if any"
endlocal
echo my batch file is %~0

Replace %~db1 using SET

I'm trying to replace a string passed as an argument to a batch file.
This works:
set PWD="%~dp1"
set "PWD=%PWD:\=/%"
Which puts the passed argument's parent directory path inside PWD variable, and then replaces \ characters to / characters.
Is there a way to achieve this one liner by execution of just one command instead of two?
Something like this: set "PWD=%~dp1:\=/%, however, that doesn't obviously work.
Please elaborate both methods, using DelayedExpansion and not using it.
Did you test this before posting it?
Your code will produce
PWD=\=/
If it produces the "correct result" then it's because you're not clearing pwd at the end of each run (usually by a setlocal directly after the #echo off) and hence the result will be NOT of the current %~dp1 but of the previous setting of pwd.
Your code first sets pwd to be "c:\whatever..." (including the quotes) PLUS the Space before the & and then sets pwd again to the prior value of pwd with \ replaced by /.
cmd will resolve any expression involving %var% first, and then executes the result. With delayedexpansion, !var! is evaluated at run-time and %var% at parse-time.
Since string-manipulation is not allowed on metavariables, the required operation cannot be condensed into a single statement; the value must be passed through an ordinary environment variable first.
To fix your statement, use
set "PWD=%~dp1" & CALL set "PWD=%%PWD:\=/%%"
which will perform the string-manipulation in a subshell after first assigning the value to pwd - moving the quote delimits the command that will be executed - following spaces after the closing quote will not be included in the value assigned.

Win10 Batch script: \Notepad++ was unexpected at this time

This batch file gives the error in the title:
if "%PROCESSOR_ARCHITECTURE%"=="x86" (
echo FOOBAR
) else (
set HOME_EDIT=%SystemDrive%\Program Files (x86)\Notepad++
)
This is on a Win10 Pro x64 system (so the test is false).
Strangely the "set HOME_EDIT..." line, if executed by itself, does NOT generate the error. And this batch file seemed to work OK a few weeks ago (oldest complaint in the book, I know, but maybe the recent Win10 Creator's Update chagned something?).
Here's the whole output:
U:\Users\Dave\data\PC setup\2017-03 PC Setup for Win10>test.bat
\Notepad++ was unexpected at this time.
U:\Users\Dave\data\PC setup\2017-03 PC Setup for Win10> set HOME_EDIT=C:\Program Files (x86)\Notepad++
U:\Users\Dave\data\PC setup\2017-03 PC Setup for Win10>
Putting quotes around the assignment certainly solves the problem, but it has nothing to do with spaces. The problem is the ) in the path is closing the ELSE block prematurely unless the path is quoted (or escaped).
Without quotes, the ELSE block becomes
) else (
set HOME_EDIT=%SystemDrive%\Program Files (x86
)
And then the \Notepad++ is indeed unexpected, causing a syntax error.
I see 3 ways to eliminate the syntax error:
1) Eliminate the parentheses and put the SET command on the same line as ELSE
else set HOME_EDIT=%SystemDrive%\Program Files (x86)\Notepad++
2) Put quotes around the assignment
) else (
set "HOME_EDIT=%SystemDrive%\Program Files (x86)\Notepad++"
)
3) Escape the closing parenthesis
) else (
set HOME_EDIT=%SystemDrive%\Program Files (x86^)\Notepad++
)
If I were to do the assignment, I would use the predefined environment variable for the folder.
) else (
set "HOME_EDIT=%ProgramFiles(x86)%\Notepad++"
)
Answering my own question:
#drescherjm was correct (in the comments) that the immediate problem was lack of quote marks ("foo") around the argument to SET. Somehow this matters when the SET is within a IF statement, even tho it doesn't otherwise.
But adding quotes just broke something else later in my batch script:
set NEW_PATH=%HOME_WINDOWS%
set NEW_PATH=%NEW_PATH%;%BinPath%
set NEW_PATH=%NEW_PATH%;%BinPath%\ffmpeg\bin
set NEW_PATH=%NEW_PATH%;%BinPath%\mplayer
set NEW_PATH=%NEW_PATH%;%BinPath%\gui
set NEW_PATH=%NEW_PATH%;%BinPath%\dll
set NEW_PATH=%NEW_PATH%;%HOME_CYGWIN%\bin
set NEW_PATH=%NEW_PATH%;%HOME_CYGWIN%\sbin
set NEW_PATH=%NEW_PATH%;%HOME_CYGWIN%\usr\bin
set NEW_PATH=%NEW_PATH%;%HOME_CYGWIN%\usr\sbin
set NEW_PATH=%NEW_PATH%;%HOME_7ZIP%
set NEW_PATH=%NEW_PATH%;%HOME_EDIT%
set NEW_PATH=%NEW_PATH%;%HOME_DIFF%
set Path=%NEW_PATH%
setx Path "%NEW_PATH%"
If HOME_EDIT has quote marks in it, this causes SETX to fail.
Here's the fix I came up with (note lines marked with "TRICK1"):
REM The following is a trick to get around spaces in the path (TRICK1)
set HOME_EDIT=%SystemDrive%\%ProgramFiles(x86)%\Notepad++
if "%PROCESSOR_ARCHITECTURE%"=="x86" (
set HOME_CYGWIN=%SystemDrive%\cygwin
set HOME_EDIT=%SystemDrive%\Program Files\Notepad++
) else (
set HOME_CYGWIN=%SystemDrive%\cygwin64
REM (TRICK1 rem this out) set HOME_EDIT=%SystemDrive%\%ProgramFiles(x86)%\Notepad++
)
The path with the spaces in it is SET as a default value prior to entering the IF statement. This way no quote marks are needed, and SETX doesn't break later.

Quotes in variable - comparison and substitution

I define variable like:
set LOGGING_MANAGER=-Djuli-logback.configurationFile=file:"%CATALINA_HOME%\conf\logback.xml"
Notice, that I wrap %CATALINA_HOME%\conf\logback.xml in double quotes, because path may contain spaces. And at execution step this variable will substitute to java program.
Example of substitution:
path_to_jre/java.exe %LOGGING_MANAGER%
Next code I can't change: (it's from catalina.bat file from Apache Tomcat)
if not "%LOGGING_MANAGER%" == "" ...
This if statement will fail, because variable contains quotes.
There I have one of 2 problems:
if statement fail (error occured).
without quotes when substitute to java program have problems (space is delimiter).
How can I change variable definition so that the path may contain spaces and if statement will work fine?
note: Not tested, but after reading the catalina.bat this is a way to deal with the problem.
If you are running this from a batch file, you can use
set LOGGING_MANAGER_FILE="%CATALINA_HOME%\conf\logback.xml"
set LOGGING_MANAGER=-Djuli-logback.configurationFile=file:%%LOGGING_MANAGER_FILE%%
set _RUNJAVA=CALL "%JRE_HOME%\bin\java.exe"
call catalina.bat
What it does is
Save the file name with quotes in a separate variable
Set the LOGGING_MANAGER variable using a escaped reference of the previous variable. As it will not be expanded, the final content of the LOGGING_MANAGER variable is -Djuli-logback.configurationFile=file:%LOGGING_MANAGER_FILE%
That way, when the if "%LOGGING_MANAGER%"=="" ... is executed, there will be not any problematic quote.
And now the set _RUNJAVA line. Inside catalina.bat, the contents of the _RUNJAVA are used to initialize the _EXECJAVA variable that will launch the server. _RUNJAVA is initialized in setclasspath.bat IF it has not been initialized by the user (code from setclasspath.bat).
rem Don't override _RUNJAVA if the user has set it previously
if not "%_RUNJAVA%" == "" goto gotRunJava
rem Set standard command for invoking Java.
rem Also note the quoting as JRE_HOME may contain spaces.
set _RUNJAVA="%JRE_HOME%\bin\java.exe"
The default value is "%JRE_HOME%\bin\java.exe", we just add a CALL batch command before to force a second iteration of the batch parser that will expand the quoted %LOGGING_MANAGER_FILE% variable in the final command line.
for %%a in ("%CATALINA_HOME%\conf\logback.xml") do set "LOGGING_MANAGER=-Djuli-logback.configurationFile=file:%%~sa"
should set logging_manager appropriately so that you don't have to "quote the filename"
for %%a in ("%CATALINA_HOME%\conf") do net use o: "\\%userdomain%\sharenameforthedrivewherecatalina_homeisresident%%~pa"
set "LOGGING_MANAGER=-Djuli-logback.configurationFile=file:o:\conf\logback.xml"
may also work - if you know the share name for the drive where catalina_home is resident and assuming o: is an unused drive.

How Do You Show All Characters In Echo On In Batch

I'm having a problem where I can't get all characters (especially special characters) to show when I try to echo them
echo 1234567890q!w#e#r$t%y^u&io()pa;s/d.f,ghjklzxcvb nmQAZXSWEDCVFRTGBNHYUJMKIOLP
When using this code in batch, it doesn't show the characters (especially the special characters) but instead say "This program is not recognizable as an internal or external command, operable program or batch file.". The reason to why this is saying this is because the lines have special characters but how do I show the special characters in echo on? Thanks people.
Neither of those lines results in that particular error message when I test them from the command line (although the first results in "Environment variable 123456etc. not defined"). You have properly quoted your "variable=value" pair, and there is no problem setting CHARSET to that value.
I suspect the problem is that when you echo %charset% the & is being evaluated as a command separator, and evaluating the string thereafter as a new command. Try retrieving the value of CHARSET in the delayed expansion style.
set "charset=1234567890q!w#e#r$t%%y^u&io()pa;s/d.f,ghjklzxcvb nmQAZXSWEDCVFRTGBNHYUJMKIOLP"
setlocal enabledelayedexpansion
echo !charset!
endlocal
By the way, if you want to include a literal percent, you should do it double (%%).

Resources