Batch character escape on ^| don't work - windows

^| Should be escape for the | character.
set good=Debug^|Win32
echo Incorrect paramets %good%
pause > nul
Why this give me error?

You have two ways to insert special characters in a variable. Escaping them:
set good=Debug^|Win32
or enclosing them between quotes:
set "good=Debug|Win32"
However, the only way to correctly show a variable value that contain special characters is using delayed expansion:
setlocal EnableDelayedExpansion
set good=Debug^|Win32
echo Incorrect paramets !good!
pause > nul

I try to add some explanations to the main problem.
As James L. noted, you have the expected content in your variable, but it's problematic to echo it.
echo Incorrect parameters %good:|=^|%
It's a problem of the parser.
After a percent expansion special characters will be parsed, if they aren't escaped by a caret or quote.
The way of James L. only works if there are only pipes, but no other special characters in the variable, as you can't replace two different characters this way.
But you could escape all special characters twice, like:
set good=Debug^^^|Win32^^^&Ampersand
echo Incorrect parameters %good%
or the same
set "good=Debug^|Win32^&Ampersand"
echo Incorrect parameters %good%
This works, but the content of good is Debug^|Win32^&Ampersand which is in the most cases not wanted.
The way of Aacini, to use delayed expansion works always with any content, as after the delayed expansion the content will not further be parsed, so the special characters are harmless.
This is one of the major advantages of the delayed expansion about percent expansion.
There is another way to use quotes like the solution of Axel Kemper.
echo "Incorrect parameters %good%"
But in the most cases the quotes shouldn't be printed.

Try using a label.
In batch, labels are preceded by the colon.
:ok
is a label.
It is not necessary to usu the colon in a goto
goto ok
goto :ok
are equivalent.

You properly escaped the | character by set good=Debut^|Win32. When you echo it, you have to replace the | character with the escape sequence, like this:
#echo off
set good=Debug^|Win32
echo Incorrect paramets %good:|=^|%
pause > nul
This makes it echo the escaped pipe (^\) instead of piping it to Win32.

use set/p for output:
#echo off&setlocal
set "good=Debug|Win32"
<nul set/p"=Incorrect parameters %good%"
echo(
output is:
Incorrect parameters Debug|Win32

The following works as intended:
set good=Debug^|Win32
echo "Incorrect parameters %good%"
pause > nul
If you omit the quotes in the echo statement, Windows tries to execute "Win32" which is impossible.

I would recommend:
echo Incorrect paramets Debug^|Win32
pause > nul

Related

Trying to concatenate the last 10 lines of a log file to a batch variable using powershell

I'm new to Windows scripting, but have quite a lot of experience in bash and python.
Here's the issue. Whenever I run this, (and this is the best result I've gotten so far) it makes it most of the way through and then errors with "The filename, directory name, or volume label syntax is incorrect."
Ignore the code designed for newlines, I'm still fighting with that as well.
setlocal EnableDelayedExpansion
set LF=^
set LAST_TEN=Here are the last 10 lines of the download log:
for /f "tokens=* usebackq" %%x in (`powershell -command "& {Get-Content download.log | Select-Object -last 10 | ForEach-Object {$_.substring(2)}}"`) do (
set LAST_TEN=!LAST_TEN!%%x
)
echo %LAST_TEN%
The reason I'm taking the substring is because some of the lines in the logfile start with < and > . I thought that was my only issue, but that is not the case. Please let me know if any more info is needed. Thank you!
Note: Your own answer shows the effective solution, but I thought I'd provide some background information.
Squashman has provided the crucial pointer:
Switching from echo %LAST_TEN% to echo !LAST_TEN! avoids problems with metacharacters (special characters such as < and >) in the variable value, which are what caused your error message.
The alternative would be to double-quote the variable reference - echo "%LAST_TEN%" - but, sadly, the double quotes are then included in the output.
In other words: If you need to echo the value of a variable that (potentially) contains metacharacters unquoted:
Place setlocal EnableDelayedExpansion at the start of your batch file.
Then reference the variable of interest as !VAR! instead of %VAR%: the delayed expansion this results in prevents the value from becoming part of the source-code line that cmd.exe parses (due to the macro-style up-front expansion that happens with %VAR%).
As an aside: Loop variables - such as %%x in your code - despite using % rather than ! as the delimiter, are of necessity always expanded in a delayed fashion, which is the reason that set LAST_TEN=!LAST_TEN!%%x worked even without the double-quoting around enclosing both the variable name and value that is normally required for literals and values of non-delayed variable references containing metacharacters (e.g.
set "LAST_TEN=a < b")
A simplified example:
#echo off
setlocal enableDelayedExpansion
:: Define a sample variable
:: Note the "..." enclosing both the name and the value.
set "var=a value with metacharacters: < > & |"
:: Thanks to using !var!, echoing the value *unquoted* works
echo !var!
Scoping setlocal enableDelayedExpansion:
One pitfall of delayed expansion is that that all ! characters are then considered part of delayed variable references, typically resulting in their quiet removal; e.g., echo hi! outputs just hi.
To escape ! characters in literal strings that should be used verbatim, you need ^^! (sic) in unquoted strings, and ^! inside "...".
The escaping is also needed for %...% variable references (e.g., echo %var:!=^^!%), but is again avoided for !...! ones.
To avoid such escaping headaches you can enable setlocal enableDelayedExpansion on demand, for a given line or block of lines, and disable it again with endlocal:
#echo off
:: Define a sample variable
:: Note the "..." enclosing both the name and the value.
set "var=a value with metacharacters: < > & |"
:: Because setlocal enableDelayedExpansion is NOT (yet)
:: in effect, the use of "!" is not a problem.
echo hi!
:: Localize the use of delayed expansion
setlocal enableDelayedExpansion
echo !var!
endlocal
:: Use of "!" is again fine.
echo hi again!
Caveat: Since setlocal creates a copy of the environment variables, which endlocal then discards, do not try to set variables between setlocal and endlocal if you need later code to see these changes.
As you're already using PowerShell, why not let it do the donkey work?
Grab the last ten lines and concatenate them within parentheses, for example:
For /F Delims^=^ EOL^= %%G In ('%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command "(Get-Content 'download.log' | Select-Object -Last 10) -Join ''"') Do Set "LAST_TEN=%%G"
Changed
echo %LAST_TEN%
to
echo !LAST_TEN!

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.

Escape percent signs in given variables

My first post, most questions already solved using this friendly provided knowldge here. But now I run out of ideas, again with a question about handling of poison characters in cmd.exe.
Let's assume there is a given string variable enclosed in double quotes. Most poison characters has already been replaced by common chars before, the left ones disturbing the script are "&", "(", ")" and "%". The string must be echoed to a file without quotes afterwards. So I had the idea to escape the poison characters tripled:
#echo off & setlocal ENABLEEXTENSIONS
SET AlbumArtist=%1
CALL :EscapePoisonChars %AlbumArtist% AlbumArtist_VDN
SET "FlacHyperLink==hyperlink^("file://%AlbumArtist_VDN%"^;"LossLess"^)")
echo %FlacHyperLink%
echo %AlbumArtist_VDN%
endlocal &GOTO:EOF
:EscapePoisonChars
#echo off & setlocal ENABLEEXTENSIONS
SET TmpString=%1
SET TmpString=%TmpString:&=^^^&%
SET TmpString=%TmpString:(=^^^(%
SET TmpString=%TmpString:)=^^^)%
endlocal&SET %2=%TmpString:~1,-1%&GOTO :EOF
When I call my script above I get the expected output - apart from the missing percent sign:
G:\YAET\20130204_Work>TryAmper.bat "100% Rock & Roll (7' UpMix)"
=hyperlink("file://100 Rock & Roll (7' UpMix)";"LossLess")
100 Rock & Roll (7' UpMix)
G:\YAET\20130204_Work>
I know that the percent can be escaped by itself. So "%%" will normally lead to a single literal "%". But it was not possible for me to find a working replace procedure for percent signs because cmd always interprets it as a variable and tries to expand it. Is this the complete wrong direction to handle this issue or just misunderstanding of variable expansion? Any hints welcome! Thanks!
Cheers, Martin
Edit
Removed own code, see below Jeb's answer for clean solution.
Thanks for help, Martin
Nice question!
At first, yes you can replace even percent signs, but not within a percent expansion, you need a delayed expansion here.
Setlocal EnableDelayedExpansion
set tmpstr=!tmpstr:%=%%!
But if you use the delayed expansion, you don't need the escapes anymore, as the delayed expansion is the last phase of the batch parser and all characters lose any special meaning.
You only need to echo with delayed expansion.
Echo !tmpvar!
EDIT: Clean solution
#echo off
setlocal DisableDelayedExpansion
REM * More or less secure getting the parameter
SET "AlbumArtist=%~1"
setlocal EnableDelayedExpansion
SET "FlacHyperLink==hyperlink("file://!AlbumArtist!";"LossLess")"
echo !FlacHyperLink!
echo !FlacHyperLink!> hugo.txt
You need disableDelayedExpansion first, to get even exclamation marks from %1.
After that, you should switch to delayed expansion and use it anywhere.

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.

Escape angle brackets in a Windows command prompt

I need to echo a string containing angle brackets (< and >) to a file on a Windows machine. Basically what I want to do is the following:
echo some string < with angle > brackets >>myfile.txt
This doesn't work since the command interpreter gets confused with the angle brackets. I could quote the whole string like this:
echo "some string < with angle > brackets" >>myfile.txt
But then I have double quotes in my file that I don't want.
Escaping the brackets ala unix doesn't work either:
echo some string \< with angle \> brackets >>myfile.txt
Ideas?
The Windows escape character is ^, for some reason.
echo some string ^< with angle ^> brackets >>myfile.txt
True, the official escape character is ^, but be careful because sometimes you need three ^ characters. This is just sometimes:
C:\WINDOWS> echo ^<html^>
<html>
C:\WINDOWS> echo ^<html^> | sort
The syntax of the command is incorrect.
C:\WINDOWS> echo ^^^<html^^^> | sort
<html>
C:\WINDOWS> echo ^^^<html^^^>
^<html^>
One trick out of this nonsense is to use a command other than echo to do the output and quote with double quotes:
C:\WINDOWS> set/p _="<html>" <nul
<html>
C:\WINDOWS> set/p _="<html>" <nul | sort
<html>
Note that this will not preserve leading spaces on the prompt text.
There are methods that avoid ^ escape sequences.
You could use variables with delayed expansion. Below is a small batch script demonstration
#echo off
setlocal enableDelayedExpansion
set "line=<html>"
echo !line!
Or you could use a FOR /F loop. From the command line:
for /f "delims=" %A in ("<html>") do #echo %~A
Or from a batch script:
#echo off
for /f "delims=" %%A in ("<html>") do echo %%~A
The reason these methods work is because both delayed expansion and FOR variable expansion occur after special operators like <, >, &, |, &&, || are parsed. See How does the Windows Command Interpreter (CMD.EXE) parse scripts? for more info.
sin3.14 points out that pipes may require multiple escapes. For example:
echo ^^^<html^^^>|findstr .
The reason pipes require multiple escapes is because each side of the pipe is executed in a new CMD process, so the line gets parsed multiple times. See Why does delayed expansion fail when inside a piped block of code? for an explanation of many awkward consequences of Window's pipe implementation.
There is another method to avoid multiple escapes when using pipes. You can explicitly instantiate your own CMD process, and protect the single escape with quotes:
cmd /c "echo ^<html^>"|findstr .
If you want to use the delayed expansion technique to avoid escapes, then there are even more surprises (You might not be surprised if you are an expert on the design of CMD.EXE, but there is no official MicroSoft documentation that explains this stuff)
Remember that each side of the pipe gets executed in its own CMD.EXE process, but the process does not inherit the delayed expansion state - it defaults to OFF. So you must explicitly instantiate your own CMD.EXE process and use the /V:ON option to enable delayed expansion.
#echo off
setlocal disableDelayedExpansion
set "line=<html>"
cmd /v:on /c echo !test!|findstr .
Note that delayed expansion is OFF in the parent batch script.
But all hell breaks loose if delayed expansion is enabled in the parent script. The following does not work:
#echo off
setlocal enableDelayedExpansion
set "line=<html>"
REM - the following command fails
cmd /v:on /c echo !test!|findstr .
The problem is that !test! is expanded in the parent script, so the new CMD process is trying to parse unprotected < and >.
You could escape the !, but that can get tricky, because it depends on whether the ! is quoted or not.
If not quoted, then double escape is required:
#echo off
setlocal enableDelayedExpansion
set "line=<html>"
cmd /v:on /c echo ^^!test^^!|findstr .
If quoted, then a single escape is used:
#echo off
setlocal enableDelayedExpansion
set "line=<html>"
cmd /v:on /c "echo ^!test^!"|findstr .
But there is a surprising trick that avoids all escapes - enclosing the left side of the pipe prevents the parent script from expanding !test! prematurely:
#echo off
setlocal enableDelayedExpansion
set "line=<html>"
(cmd /v:on /c echo !test!)|findstr .
But I suppose even that is not a free lunch, because the batch parser introduces an extra (perhaps unwanted) space at the end when parentheses are used.
Aint batch scripting fun ;-)
In order to use special characters, such as '>' on Windows with echo, you need to place a special escape character before it.
For instance
echo A->B
will no work since '>' has to be escaped by '^':
echo A-^>B
See also escape sequences.
There is a short batch file, which prints a basic set of special character and their escape sequences.
Escaping the brackets ala unix doesn't
work either:
echo some string \< with
angle \> brackets >>myfile.txt
The backslash would be considered the start of a absolute pathname.
You can also use double quotes to escape special characters...
echo some string "<" with angle ">" brackets >>myfile.txt

Resources