Batch String Concatenation - windows

I am trying to create a batch string like this: >abcd_
I have a variable called soeid, with value as abcd. So this is what i am doing, but it does not work.
set soeid=abcd
set "val1=>"
set "val2=_"
set "str=%val1%%soeid%%val2%"
echo %str%

I'm sure it is working just fine. To prove it, add SET STR after you define the value, and you will see the correct value.
The problem you are having is when you try to echo the value, the line that is executing becomes: echo >abcd_. The > is not quoted or escaped, so it is simply taking the ouput of ECHO with no arguments and redirecting it to a file named "abcd_"
If you don't mind seeing quotes, then change your line to echo "%str%" and it will work.
The other option is to enable and use delayed expansion (I'm assuming this is a batch script code, and not executing on the command line)
setlocal enableDelayedExpansion
set soeid=abcd
set "val1=>"
set "val2=_"
set "str=%val1%%soeid%%val2%"
echo !str!
Normal %var% expansion occurs early on while the interpreter is parsing the line. Delayed !var! expansion occurs at the end just before it is executed. The redirection is detected somewhere in the middle. That is why the normal expansion doesn't work - the interpreter sees the expanded > character and interprets it as the output redirection operator. The delayed expansion hides the > character from the interpreter until after redirection is parsed.
For more info about delayed expansion, type SET /? from the command line and read starting with the paragraph that starts with "Finally, support for delayed environment variable expansion...".

Related

Why doesn't IF branch properly?

I already asked this in SS64.
I'm trying to use IF DEFINED to control branching, but it doesn't work. I will start with an example
setLocal
if defined _var (
:: _var is NOT defined, so the next line should not be executed
if %_var%=="test" echo %_var% is defined
)
endLocal
Processing stops when it hits the ECHO statement and exits with "echo was unexpected at this time." error message.
The problem is that the nested IF statement is being executed, and it shouldn't be because _var is not defined. How can I fix this?
Thanks,
Shane.
The real problem you have is just that you did not match the if statement correctly. To explain, see the following example.
if a == "a"..
vs
if "a"=="a"..
So in you example you were trying to match:
if test=="test"..
The reason we use double quotes on both sides of the variable is to ensure we eliminate any possible whitespace which might become part of the values you are testing.
So this version should be fine. I added the /i switch to match test case insensitive, so it will match test in any case combination. TesT, tEst, TEST, test etc.:
setLocal
set /p "_var=Enter Variable name: "
set "_var=%_var:"=%"
if defined _var (
:# rem _var is NOT defined, so the next line should not be executed
if /i "%_var%"=="test" echo %_var% is defined
)
endLocal
There are multiple issues with this small code block.
The first issue is using an invalid label with :: to write a comment inside a command block starting with ( and ending matching with ). A label is not allowed inside a command block. A line starting with : after 0 or more spaces/tabs results in undefined behavior on execution of the command block. Undefined behavior means it can work by chance, but it can also fail by chance. The execution behavior is undefined.
The command for a comment is REM. Run in a command prompt window rem /? for help on this command.
Please note that REM is a command and for that reason a comment line with REM is parsed by Windows command processor like any other command line which is important on comment line contains %variable% or operators like &|<>. So be careful on what is written in comment text.
The second issue is that the comment itself is not correct.
_var is NOT defined, so the next line should not be executed
This comment is wrong because the environment variable _var is definitely defined on cmd.exe reaching this line after execution of the command if defined _var with a positive result.
The third issue is that if %_var%=="test" is syntactically not 100% correct. There are missing the spaces around the comparison operator ==. But this syntax error is automatically detected by cmd.exe on processing the batch file with automatic correction by inserting the two missing spaces around operator ==. This can be seen on debugging the batch file.
The fourth issue is that IF compares two strings always with including the double quotes on string comparison. So on usage of if %_var%=="test" or syntactically correct if %var% == "test" the condition is only true if assigned to environment variable _var is the string "test" with the double quotes. This string comparison evaluates to false in all other cases on _var defined if not resulting in a syntax error before, see next issue.
Please read my answer on Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files explaining very detailed how a string comparison is done by command IF.
The fifth issue is that Windows command processor parses the entire command block starting with ( and ending with matching ) before executing the command IF.
The inner IF without environment variable _var defined results on parsing the command block in the line:
if =="test" echo is defined
cmd.exe detects a serious syntax error on this command line and exits batch file processing because of this syntax error before executing the outer IF condition.
One solution would be enclosing %var% in double quotes, i.e. use in batch file:
if "%_var%" == "test" echo %_var% is defined
This IF command line is correct if the environment variable _var is not defined on parsing the command block.
But this solution does not work if _var is defined and contains one or more " because of the double quotes result again in an invalid syntax for command line with command IF.
Further echo does not result in printing the value of the environment variable _var if the string assigned to the environment variable contains one of these characters &|<>. Well, echo is not executed in this case as the string comparison evaluates to false.
Therefore a better solution is enabling delayed expansion and referencing the value of environment variable _var with delayed environment variable expansion to avoid that the value of this environment variable modifies the command line to execute by cmd.exe after parsing entire command block.
setlocal EnableExtensions EnableDelayedExpansion
if defined _var (
rem _var is defined, so the next line should be executed.
if "!_var!" == "test" echo _var is defined with !_var!.
)
endlocal
The command extensions must be also enabled because otherwise if defined would not be supported by command IF. The command extensions are by default enabled, but it is better to write a batch file with no dependency on settings or defaults defined outside of the batch file as far as possible.
Well, the echo command could be also written with:
echo _var is defined with test.
It is not possible that echo prints here something other than test on using !_var!.
Another solution is to avoid the usage of a command block and make sure that the string value assigned to environment variable _var does not contain any " and output the value of this environment variable enclosed in double quotes to avoid that &|<> affect the execution of echo command line.
setlocal EnableExtensions DisableDelayedExpansion
if not defined _var goto Label
rem _var is defined, so the next line should be executed.
rem Remove all double quotes from string value of environment variable.
rem It is important to use double quotes around argument string of the
rem command SET as otherwise a string with "&|<>" would be not correct
rem assigned to the environment variable.
set "_var=%_var:"=%"
rem It could happen that _var is not defined anymore. But in this case
rem it is no problem anymore that cmd.exe replaces all %var% by nothing
rem on entire command line before executing the IF condition as the
rem remaining commands on this command line with enclosing the string
rem value of _var in double quotes is always syntactically correct even
rem on string value containing one or more of the operators "&|<>".
if /I "%_var%" == "test" echo _var is defined with "%_var%".
:Label
endlocal
The string comparison is done here case-insensitive.
See also:
Single line with multiple commands using Windows batch file
Microsoft article about Using command redirection operators
It must be always made sure that a user input string or a string passed to the batch file as argument or a string determined by the batch file itself for example from a file/folder name/path is enclosed in double quotes and does itself not contain double quotes for being correct processed by the batch file. While the characters |<> are not allowed in a file/folder name, & is allowed in a file/folder name and results in many batch files on being interpreted as AND operator on file/folder string not enclosed in double quotes, even on simply echoing the file/folder name.
See also the Microsoft documentation page Naming Files, Paths, and Namespaces.
It is very important to understand on writing a batch file How does the Windows Command Interpreter (CMD.EXE) parse scripts? It must be always taken into account how a command line finally looks on execution with environment variable references expanded during parsing phase of a command line or an entire command block. The finally executed command line after parsing must be of valid syntax or cmd.exe exits batch file processing.

How to output special characters like angle brackets into an HTML/XHTML/XML file?

I want to put the following content into the file: <ScriptFile Make="3">
It fails for the reason of the string containing the angle brackets < and > and the double quote character ".
I have tried escaping the characters following way: ^<ScriptFile Make=""3""^>
It worked, but the output in the file was exactly the same as the escaped string.
The code snippet:
#echo off
set TEMP="^<ScriptFile Make=""3""^>"
echo %TEMP% > gen.xml
pause
How can I output the string value of TEMP variable into file gen.xml without loosing the double quotes and the angle brackets?
You can extract the angle brackets out of the variable, like this:
#echo off
set TEMP1=ScriptFile Make="3"
echo ^<%TEMP1%^> > gen.xml
pause
This way, the brackets can be escaped properly, you do not need any special escaping for the string put in the variable and the gen.xml looks like expected:
D:\temp>type gen.xml
<ScriptFile Make="3">
This worked for me:
#echo off
set "TEMP=^<ScriptFile Make="3"^>"
echo %TEMP% > gen.xml
pause
Another method would be to use delayed expansion:
#echo off
set "TEMP=<ScriptFile Make="3">"
setlocal EnableDelayedExpansion
echo !TEMP! > gen.xml
endlocal
pause
TEMP should not be used as environment variable name because TEMP is an important environment variable predefined by Windows. It has as value the name of the directory for temporary files of current user account with complete path. For details see Wikipedia article Windows Environment Variables.
One method is using delayed expansion as suggested also by Andriy M.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "TempVar=<ScriptFile Make="3">"
echo !TempVar!>gen.xml
endlocal
First a local environment is created for the next two command lines with command extensions and delayed expansion of environment variables enabled which are both needed here. Command extensions are enabled by default, but delayed expansion is disabled by default. See this answer explaining in detail what the commands SETLOCAL and ENDLOCAL do.
The string <ScriptFile Make="3"> is assigned next to environment variable TempVar. There is no need to escape the angle brackets or the double quotes. For a detailed explanation why there is no need to escape anything in this string and why first double quote character is left of variable name and not after equal sign read this answer.
The value of the environment variable is output by command ECHO with redirecting this output with the redirection operator > into the file gen.xml using delayed expansion.
There is no space character between second exclamation mark ! and redirection operator >. This avoids writing also a trailing space after <ScriptFile Make="3"> into the file. 1 or more space characters between > and file name gen.xml are ignored on parsing this command line. But any whitespace character left of redirection operator > is also output by command ECHO and for that reason also written into the file.
Another method is not using an environment variable at all and escape the angle brackets with character caret ^ as demonstrated below:
#echo off
echo ^<ScriptFile Make="3"^>>gen.xml
endlocal
Double quotes must not be escaped on using command ECHO as in this special case the double quote characters are interpreted as literal characters.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
endlocal /?
set /?
setlocal /?
And read also the Microsoft TechNet article Using command redirection operators.

Windows batch file syntax using exclamation mark

While checking the details of the axis2server.bat file in Axis2 binary distribution I see one of the line containing text something like:
FOR %%c in ("%AXIS2_HOME%\lib\*.jar") DO set AXIS2_CLASS_PATH=!AXIS2_CLASS_PATH!;%%c
What does the part below with 2 exclamation marks mean?
!AXIS2_CLASS_PATH!
Names with in % mean variables, not sure what ! mark mean in a batch file.
When you enable delayed expansion and change or set a variable within a loop then the !variable! syntax allows you to use the variable within the loop.
A drawback is that ! becomes a poison character for delayed expansion.
As foxidrive mentioned, this is related to delayed expansion. You can find more information by running help set in a cmd prompt, which has the following explanation:
Finally, support for delayed environment variable expansion has been
added. This support is always disabled by default, but may be
enabled/disabled via the /V command line switch to CMD.EXE. See CMD /?
Delayed environment variable expansion is useful for getting around
the limitations of the current expansion which happens when a line
of text is read, not when it is executed. The following example
demonstrates the problem with immediate variable expansion:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "%VAR%" == "after" #echo If you see this, it worked
)
would never display the message, since the %VAR% in BOTH IF statements
is substituted when the first IF statement is read, since it logically
includes the body of the IF, which is a compound statement. So the
IF inside the compound statement is really comparing "before" with
"after" which will never be equal. Similarly, the following example
will not work as expected:
set LIST=
for %i in (*) do set LIST=%LIST% %i
echo %LIST%
in that it will NOT build up a list of files in the current directory,
but instead will just set the LIST variable to the last file found.
Again, this is because the %LIST% is expanded just once when the
FOR statement is read, and at that time the LIST variable is empty.
So the actual FOR loop we are executing is:
for %i in (*) do set LIST= %i
which just keeps setting LIST to the last file found.
Delayed environment variable expansion allows you to use a different
character (the exclamation mark) to expand environment variables at
execution time. If delayed variable expansion is enabled, the above
examples could be written as follows to work as intended:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" #echo If you see this, it worked
)
set LIST=
for %i in (*) do set LIST=!LIST! %i
echo %LIST%
I wanted to hand over a string containing a "!" as parameter (for imageMagick) and the ! was of course interpreted as syntax which broke my script. The solution was for me, change my string from
"Hello World!"
to (just added a ^ before the !):
"Hello World^!"
I found this trick by reading here: https://www.robvanderwoude.com/escapechars.php

Difference between %variable% and !variable! in batch file

I am writing a batch file where I need to output a string containing '!' to another file. But when I echo that string to another file, it removes "!" from the output.
Eg:
Input:
set LINE=Hi this is! output
echo !LINE!>>new_file.txt
Output in new_file.txt is:
Hi this is output
Also, if input is
set LINE=Hello!! this is output!!
echo !LINE!>>new_file.txt
Output in new_file.txt:
Hello
Hence, it skips the ! (Exclamation mark) from the output to the new_file.
If I use %LINE%, then it simply displays "echo is on" to the output file.
Please suggest a way to overcome this problem.
If you have delayed expansion enabled and want to output an exclamation mark, you need to escape it.
Escaping of exclamation marks needs none, one or two carets, depending on the placement.
#echo off
REM No escaping required, if delayed expansion is disabled
set test1=Test1!
setlocal EnableDelayedExpansion
REM One caret required
REM Delayed expansion uses carets independent of quotes to escape the exclamation mark
set "test2=Test2^!"
REM Two carets required
REM The first caret escapes the second caret in phase2 of the parser
REM Later in the delayed expansion phase, the remaining caret escapes the exclamation mark
set test3=Test3^^!
echo !test1!
echo !test2!
echo !test3!
The difference between !var! and %var% in blocks is explained at DOS batch: Why are my set commands resulting in nothing getting stored?
An explanation of the batch parser can be found at How does the Windows Command Interpreter (CMD.EXE) parse scripts?
It seems you have called SETLOCAL EnableDelayedExpansion somewhere higher in the code. Take a look here to see what the effects from that are.

Why does a specific windows batch parameter cause a crash?

Contents of test.bat are:
setlocal EnableExtensions EnableDelayedExpansion
set param1=%~1
echo %param1%
Can someone explain why test.bat "^^!^^^&^^^^" makes the cmd window crash but test.bat "^^^&^^^^" has an expected result of setting &^ to variable param1?
I can do test.bat "pass^^!word" and I get the expected result of pass!word.
Update: test.bat "^^!^^^^^&^^^^^^^^" works. But I'm not completely sure why. This gets interpreted to set param1=^!^^&^^^^. Why does ^ need ^^^ in front of it?
You got many problems, as the special characters will be evaluated mulitple times in your case.
First in the set command, the special character phase will reduce your string "^^!^^^&^^^^" to
^!^&^^
But as delayed expansion is enabled and your string contains an exclamation mark,
another phase will be enabled to reduce the carets again to.
!&^
At this point param1 contains !&^, you can test it with set param1
But as you try to echo the value with echo %param1% another expansion will be executed.
And now you get a problem, as %param1% will expand to !&^,
The exclamation mark will be removed, as the second exlamation mark is missing for expanding a variable,
the ampersand will be treated as new command separator and
the ending caret will be treated as multiline character.
echo ! & ^<next line>
It's much safer to use the delayed expansion here, as this never change the content, as this phase is the last one of the parser.
setlocal EnableDelayedExpansion
set param1=%~1
set param1
echo !param1!
And all these explanations can be found at How does CMD.EXE parse scripts?
It is because the escape character for the Windows shell is ^, so:
"^^!^^^^^&^^^^^^^^"
Each ^^ becomes ^
Each ^& becomes &
So finally you will get:
"^!^^&^^^^"

Resources