As far as I know, I need to escape every escape characters when echoing them. The ^ method works fine for a few echoes. (which should be something like:)
#echo ^|
#echo ^> ^>^>
However, when there are a lot of characters to escape, the ^ method won't work anymore. So, my question is:
Are there any ways escape all special characters without "spamming" the caret?
As mentioned by Mofi, you could use variables with delayed expansion to echo any content.
There are some more possible ways.
1) Disappearing quotes
for /F %%^" in ("""") do (
echo %%~"Hello^you
echo %%~"Line2 |&<>
)
This works as the %~" will quote the rest of the line, but after expansion it will disappear
2) When you want to echo multiple lines you should read about the different variants of heredoc
3) The MagicEcho can display any content without escaping or doubling percent signs like
%magicEcho% "^!%path%<>" ^%!%<> ^
Well, there is no need to escape redirection operators and other special characters listed in last paragraph in help output by running cmd /? in a command prompt window on last help page when the string to output is enclosed in double quotes.
But using " on line with ECHO command results in having also the double quote output.
There are several solutions.
The first one is assigning the string to output to an environment variable and output the value of the environment variable using delayed expansion.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "Line=pipe = | and percent sign = %% and exclamation mark ^!"
echo !Line!
set "Line=redirection operators: < and > and >>"
echo !Line!
endlocal
Or a little bit shorter, but not so good readable:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "Line=pipe = | and percent sign = %% and exclamation mark ^!" & echo !Line!
set "Line=redirection operators: < and > and >>" & echo !Line!
endlocal
Note: % and ! must be nevertheless escaped with another % and with ^ to be interpreted as literal character in string assigned to environment variable Line.
Another solution using a subroutine PrintLine:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
call :PrintLine "pipe = | and percent sign = %%%% and exclamation mark !"
call :PrintLine "redirection operators: < and > and >>"
endlocal
goto :EOF
:PrintLine
set "Line=%~1"
setlocal EnableDelayedExpansion
echo !Line!
endlocal
goto :EOF
The disadvantages of this solution are:
A percent sign must be defined with 4 percent signs to be finally printed as literal character.
It is slower because of usage of SETLOCAL and ENDLOCAL on printing each line.
Read this answer for details about the commands SETLOCAL and ENDLOCAL.
One more solution according to comment by JosefZ uses command FOR for an implicit delayed expansion:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for %%I in (
"pipe = | and percent sign = %% and exclamation mark !",
"redirection operators: < and > and >>"
) do echo %%~I
endlocal
The lines to output are specified in a comma separated list of double quoted strings for being processed by FOR.
It has the big advantage that just the percent sign must be escaped with an additional percent sign on delayed expansion being disabled. But the string to output can't contain a double quote with exception of "" within string.
Thanks JosefZ for this contribution.
Other great solutions are provided by jeb in his answer.
Related
I have a bat script and the first arg passed in is:
"C:\aaa^bbb\xxx^yyy.txt"
I wish to get the file and and containing dir (head):
set winhead=%~dp1
set winfile=%~nx1
echo winhead: %winhead%
echo winfile: %winfile%
Prints
winhead: 'C:\aaabbb\'
winfile: 'xxxyyy.txt'
I see the carets ^ where dropped. Okay I will just escape them:
set Pathin=%1
set Pathin=%Pathin:^=^^%
echo Pathin: %Pathin%
Prints
Pathin: "C:\aaa^^bbb\xxx^^yyy.txt"
Now to extract filename and head:
set winhead=%~dpPathin%
set winfile=%~nxPathin%
or
set winhead=%~dpPathin
set winfile=%~nxPathin
Does not perform the modifier, but just returns the literal string. After some research I discovered the modifiers can only be used on args, i.e. %1 in this case.
So is it a bug that the modifiers don't handle carets correctly? How does one extract parts of the full path for files one may have created in explorer (i.e. those which contain carets)?
The ^ Circumflex Accent (so-called caret) works as an Escape character. If you need to take a caret literally in a variable, escape it using double quotes (set "winhead=%~dp1") and use it either using double quotes (echo "%winhead%") or utilizing delayed expansion (echo !winhead!).
Sample script:
#ECHO OFF
echo supplied parameters: %*
SETLOCAL EnableExtensions DisableDelayedExpansion
set "winhead=%~dp1"
set "winfile=%~nx1"
echo winhead - percent expansion: %winhead% "%winhead%"
echo winfile - percent expansion: %winfile% "%winfile%"
SETLOCAL EnableDelayedExpansion
echo winhead - delayed expansion: !winhead! "!winhead!"
echo winfile - delayed expansion: !winfile! "!winfile!"
ENDLOCAL
Output: .\SO\62849275.bat "C:\aaa^bbb\xxx^yyy.txt"
supplied parameters: "C:\aaa^bbb\xxx^yyy.txt"
winhead - percent expansion: C:\aaabbb\ "C:\aaa^bbb\"
winfile - percent expansion: xxxyyy.txt "xxx^yyy.txt"
winhead - delayed expansion: C:\aaa^bbb\ "C:\aaa^bbb\"
winfile - delayed expansion: xxx^yyy.txt "xxx^yyy.txt"
You can escape a caret using an additional caret ("C:\aaa^^bbb\xxx^^yyy.txt"), or utilize call command behaviour: If the CALL command contains a caret character within a quoted string, the carets will be doubled:
CALL .\SO\62849275.bat "C:\aaa^bbb\xxx^yyy.txt"
supplied parameters: "C:\aaa^^bbb\xxx^^yyy.txt"
winhead - percent expansion: C:\aaa^bbb\ "C:\aaa^^bbb\"
winfile - percent expansion: xxx^yyy.txt "xxx^^yyy.txt"
winhead - delayed expansion: C:\aaa^^bbb\ "C:\aaa^^bbb\"
winfile - delayed expansion: xxx^^yyy.txt "xxx^^yyy.txt"
Side note. The ^ character (Circumflex Accent, U+005E) used to call caret for its visual similarity with the character ‸ (Caret, U+2038).
If you can change your input then pass "C:\aaa^^^bbb\xxx^^^yyy.txt" with the following:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "winhead=%~dp1"
Set "winfile=%~nx1"
Echo winhead: %winhead%
Echo winfile: %winfile%
Pause
If you cannot change the input, use "C:\aaa^bbb\xxx^yyy.txt" and enable delayed expansion:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "winhead=%~dp1"
Set "winfile=%~nx1"
SetLocal EnableDelayedExpansion
Echo winhead: !winhead!
Echo winfile: !winfile!
EndLocal
Pause
I've a code like below:
FOR /F "tokens=*" %%B IN ('%*') DO (
ECHO %%B
SET data=%%B
ECHO %data%
)
The %%B contains one or more than one | character, for example the value of %%B is First | Second | Third. The SET data=%%B or SET data="%%B" function doesn't work. When %data% is called back, it shows Echo is off.
I need to remove the | character from %%B and save it to a %data% variable. But I don't know how?
I will be grateful if any solution has been provided from anyone....
The first solution is using:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /F delims^=^ eol^= %%B in ('%*') do (
set "data=%%B"
set "data=!data:|=!"
echo(!data!
)
endlocal
tokens=* results in getting assigned to loop variable B the entire line with leading spaces/tabs removed while delims= results in getting assigned to the loop variable B the entire line including leading spaces/tabs. So the definition of an empty list of delimiters is better in general.
FOR ignores by default empty lines and lines starting with a semicolon because of ; is interpreted by default as end of line character. For that reason the uncommon, not double quoted option string delims^=^ eol^= is used here to define an empty list of delimiters and no end of line character. The caret character ^ is used to escape the next character to get it interpreted as literal character and not as argument separator although not enclosed in a double quoted argument string.
But there is one problem with this solution: A line containing one or more exclamation marks ! is not processed correct because Windows command processor interprets on command line set "data=%%B" each ! as begin/end of a delayed environment variable reference and replaces the string between two exclamation marks by the current value of the environment variable with that name or nothing on no variable existing with such a name and removes everything after ! if there is no more !.
There are at least three solutions.
The first one is enabling and disabling delayed expansion within the loop.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F delims^=^ eol^= %%B in ('%*') do (
set "data=%%B"
setlocal EnableDelayedExpansion
set "data=!data:|=!"
echo(!data!
endlocal
)
endlocal
Please read this answer for details about the commands SETLOCAL and ENDLOCAL as they do much more than just toggling delayed expansion on/off in the loop.
( between echo and value of environment variable data is used in case of a line contains only | without or with additionally just spaces/tabs resulting in data becoming either undefined or a string consisting only of spaces/tabs. A space between echo and !data! would be on execution just the command echo with an ignored space and 0 or more spaces/tabs resulting in getting output ECHO is off. instead of an empty line or a line with just spaces/tabs. The opening round bracket prevents that and is interpreted by cmd.exe as separator between command echo and its argument string which begins in this case always with (. ECHO ignores the first character of the argument string on output as long as the argument string is not /? and so ( is never output.
The second solution is using a subroutine:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F delims^=^ eol^= %%B in ('%*') do (
set "data=%%B"
call :ProcessLine
)
endlocal
goto :EOF
:ProcessLine
set "data=%data:|=%"
echo(%data%
goto :EOF
Note: echo %data% can result in unexpected behavior if the line contains characters like the redirection operators < and > or the operator & as Windows command processor executes echo(%data% after substituting %data% with current string of environment variable data. Even set "data=%data:|=%" can be problematic depending on line containing " and <>&. So this solution is really not safe.
See also Where does GOTO :EOF return to?
The third solution is using a "double percent" environment variable reference and using command CALL to force a double parsing of the command line containing %%variable%% instead of just %variable% as usual.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F delims^=^ eol^= %%B in ('%*') do (
set "data=%%B"
call set "data=%%data:|=%%"
call echo(%%data%%
)
endlocal
Note Also this solution is not really safe depending on the data as the second solution.
See also How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Windows command processor is designed for executing commands and applications and not for reformatting or processing CSV files using vertical bar as delimiter/separator.
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.
call /?
echo /?
endlocal /?
for /?
goto /?
set /?
setlocal /?
And read also answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line? for the reason using the syntax set "variable=value" instead of set variable=value or set variable="value".
In the following script i pass a qouted string to a subroutine and unquote it (with a tilde when accessing the parameter) to output it unquoted. Unfortunately my attempt to escape greater then sign(s) doesn't work:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
set foo="Argument -> Not provided^!"
call :output_actual_error !foo!
ENDLOCAL
EXIT /B 0
:output_actual_error
SETLOCAL DISABLEDELAYEDEXPANSION
set bar=%~1
set bar=%bar:>=^>%
echo %bar%
ENDLOCAL
EXIT /B 0
Output:
Argument - provided!
Expected:
Argument -> Not provided!
Please be aware that the code is just for illustration and DOES NOT represent the actual implementation.
So, how can escape the greater-than sign and echo the string without double?
Use
set "bar=%~1"
set "bar=%bar:>=^>%"
A solution is already provided by this answer. However, I want to show you how I would write the code to make it as safe as possible against all combinations of all kinds of special characters, by using proper quotation and enabling and applying delayed expansion only where it is really necessary and disabling it where it is disturbing or unsafe:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem /* No delayed expansion during immediate variable assignment,
rem so escaping of exclamation marks is not required;
rem quotation in a way not to become part of the value: */
set "foo=Argument -> Not provided!"
setlocal EnableDelayedExpansion
rem /* Delayed expansion during reading of variable;
rem quote argument value here to protect white-spaces: */
call :output_actual_error "!foo!"
endlocal
endlocal
exit /B 0
:output_actual_error
rem /* No delayed expansion during argument expansion (%);
rem quotation in a way not to become part of the value: */
setlocal DisableDelayedExpansion
set "bar=%~1"
setlocal EnableDelayedExpansion
rem // Delayed expansion during reading of variable:
echo(!bar!
endlocal
endlocal
exit /B 0
This basically avoids delayed expansion during variable assignment and %-expansion and uses delayed expansion whenever a variable is read.
There still remain risks of failure:
One is introduced by call, because it doubles quoted carets (^).
Another one is represented by the argument expansion (%~1), which may cause trouble with quotes, particularly when they appear unbalanced.
Can someone give me an example of where a batch script would act differently with or without delayed expansion? Are there any situations where you would NOT want to use delayed expansion? Thanks.
Look at the following examples...
Example 1: The following code DOESN'T use delayed expansion, so the variables in the for loop are expanded only one time. This means that %Count% will always expand to 0 in each iteration of the loop, no matter what we do to it with the set command:
#echo off
set COUNT=0
for %%v in (1 2 3 4) do (
set /A COUNT=%COUNT% + 1
echo Count = %COUNT%
)
pause
So this script will output:
Count = 0
Count = 0
Count = 0
Count = 0
This is not how this loop is supposed to work.
Example 2: On the other hand, if we use delayed expansion, we have the following script, which will run as expected.
setlocal ENABLEDELAYEDEXPANSION
set COUNT=0
for %%v in (1 2 3 4) do (
set /A COUNT=!COUNT! + 1
echo Count = !COUNT!
)
pause
and, as expected, it will output:
Count = 1
Count = 2
Count = 3
Count = 4
When you use the ENABLEDELAYEDEXPANSION, and expand a variable using ! instead of %, the variable is re-expanded each time, and everything works as it's supposed to.
I wanted to add a great example on how "EnableDelayedExpansion" (EDE) can be useful outside of the ubiquitous FOR loop examples.
Here is a line of earthquake data that I wish to parse (I call it it 1line.txt)
ak_11574812 2015.04.29.193822 62.9525 -148.8849 1.0 9.5 1 49km S of Cantwell, Alaska
The problem I ran into was that last segment of this line does not always start at the same column number. So I needed to create a flexible SET command that will accurately pluck out the last segment of this line.
ECHO OFF
setlocal enableDelayedExpansion
set where=72
set /p line=<1line.txt
set locate=!line:~%where%,28!
echo %locate%
EDE allows me to place a variable (where) inside another variable (line). EDE will translate the variable bracketed by % first, then process the variable bracketed by ! and (in this case) push out the results into the "locate" variable.
Max's answer gives an example of where a batch script would act differently with or without delayed expansion.
For the sake of completeness, let's answer another part of the question and show a situation where you would NOT want to use delayed expansion when your data contain an exclamation mark ! (and show two ways of processing such data):
#ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
set "_auxFile=%temp%\%~n0.txt"
rem create multiline sample file
>"%_auxFile%" ( for /L %%G in (1,1,3) do echo line %%G is 100%% valid! Sure! Hurrah!)
rem create one-line sample file
>"%_auxFile%" echo this line is 100%% valid! Sure! Hurrah!
echo(
echo --- file content
type "%_auxFile%"
echo(
SETLOCAL EnableDelayedExpansion
echo --- enabled delayed expansion chokes down unescaped exclamation marks^^^! "^!"
for /F "usebackq delims=" %%G in ("%_auxFile%") do (
set "_auxLine=%%~G"
echo loop var=%%~G
echo _auxLine=!_auxLine!
)
ENDLOCAL
echo(
SETLOCAL DisableDelayedExpansion
echo --- toggled delayed expansion works although might be laborious!
for /F "usebackq delims=" %%G in ("%_auxFile%") do (
set "_auxLine=%%G"
echo loop var=%%G
SETLOCAL EnableDelayedExpansion
echo _auxLine=!_auxLine!
ENDLOCAL
)
ENDLOCAL
echo(
SETLOCAL DisableDelayedExpansion
echo --- keep delayed expansion DISABLED: use CALL command!
for /F "usebackq delims=" %%G in ("%_auxFile%") do (
set "_auxLine=%%G"
echo loop var=%%G
call :ProcessVar
)
ENDLOCAL
rem delete the sample file
del "%_auxFile%"
ENDLOCAL
goto :eof
:ProcessVar
echo _auxLine=%_auxLine%
echo WARNING: neither !_auxLine! nor %%G loop variable is available here!
goto :eof
Note that above script shows proper ways of escaping
% percent sign by %% doubling it (delayed expansion does not matter), and
! exclamation mark if delayed expansion is enabled:
"^!" if enclosed in a pair of double quotes, then use the cmd and batch-script general escape character ^ caret;
^^^! otherwise, use three ^ carets.
Output:
==> D:\bat\SO\10558316.bat
--- file content
this line is 100% valid! Sure! Hurrah!
--- enabled delayed expansion chokes down unescaped exclamation marks! "!"
loop var=this line is 100% valid Hurrah
_auxLine=this line is 100% valid Hurrah
--- toggled delayed expansion works although might be laborious!
loop var=this line is 100% valid! Sure! Hurrah!
_auxLine=this line is 100% valid! Sure! Hurrah!
--- keep delayed expansion DISABLED: use CALL command!
loop var=this line is 100% valid! Sure! Hurrah!
_auxLine=this line is 100% valid! Sure! Hurrah!
WARNING: !_auxLine! as well as %G loop variables are not available here!
==>
As pointed in the answer the main usage of the delayed expansion is the setting and accessing variables in brackets context.
Though it can be useful in another situations too.
Parametrizing substring and string substitution:
#echo off
setlocal enableDelayedExpansion
set "string=test string value"
set start=5
set get_next=6
echo #!string:~%start%,%get_next%!#
set "search_for=string"
set "replace_with=text"
echo #!string:%search_for%=%replace_with%!#
the output will be:
#string#
#test text value#
though this can be achieved with additional call this way is more performant
Using shift command within brackets parameterized argument access
#echo off
echo first attempt:
(
echo "%~1"
shift
echo "%~1"
)
::now the shift command will take effect
setlocal enableDelayedExpansion
echo second attempt:
(
set /a argument=1
call echo %%!argument!
shift
call echo %%!argument!
)
the output will be:
first attempt:
"first argument"
"first argument"
second attempt:
"second argument"
"third argument"
As you can see parameterized argument access can be done only with delayed expansion.
Using for tokens (or function arguments) for parameterization
One more approach for mixing !s and %s this could be useful for nested loops:
#echo off
setlocal enabledelayedexpansion
set begin=2
set end=2
set string=12345
for /f "tokens=1,2" %%A in ("!begin! !end!") do set "string2=!string:~%%A,%%B!"
echo !string2!
endlocal
as you can see now the for command tokens are used as parameters.
Several answers here answer the "How to use delayed expansion?" question or what happen if you don't use delayed expansion. However, the second question is "Are there any situations where you would NOT want to use delayed expansion?" and a couple answers take this question as "how to avoid the problems caused by using delayed expansion?"
My answer answers the question as I understand it: "In which situations is better to NOT use delayed expansion (instead of use it)?"
If you want to exchange the contents of two variables, the simplest way to perform this is using the %standard% variable expansion:
set "var1=%var2%" & set "var2=%var1%"
The way that the %standard% expansion works makes possible to achieve this replacemenmt without using any auxiliary variable. As far as I know, the Batch-file "programming language" is the only one that allows to perform this exchange in this way, that is, making good use of a language "feature" (not via a specialized "exchange" instruction/statement).
I want to execute some program passing an argument. The argument changes depending of the day of week and it is an url
Code:
#echo off
setlocal enableDelayedExpansion
for /f %%a in ('wmic path win32_localtime get dayofweek /format:list ^| findstr "="') do (set %%a)
if %dayofweek% == 7(
EXIT
)
set link="http://backlog.telecom.pt/maps/selector/download?map_name=workline_fornecedores&organization_id=1"
if %dayofweek%==5 (
set link="http://backlog.telecom.pt/maps/selector/download?map_name=all_fornecedores&organization_id=1"
)
REM start /wait D:\Planview\Services\BackLog_Integration_Client_Service\Backlog_Integration_Client_Exe\Backlog_Integration_Client_Exe.exe %link%
REM timeout /t 600 /nobreak > NUL
REM start D:\Planview\Services\PV_Backlog_ProcessData_Service\PV_Backlof_ProcessData_Exe\PV_Backlog_ProcessData_Exe.exe
I read that ^ before & would work to escape the & char, but for me it never did and the only way i managed to do it was enableDelayedExpansion and encapsulate the url in ", but this brought me a problem.. my variable instead of having the url has "url".
I tried to do set link=%link:"% but it did not worked.
I'll try to give you some advice via simple examples:
#setlocal enableDelayedExpansion
rem This fails because & is a "poison" character (an instruction or operator)
echo abc&xyz
rem This succeeds because & is escaped
echo abc^&xyz
rem This succeeds because & is quoted
echo "abc&xyz"
rem This succeeds, but it consumes the escape: stored value = abc&xyz
set test=abc^&xyz
rem So this fails because & is not escaped
echo %test%
rem You could "fix" above by double escaping when doing SET so stored value = abc^&xyz
rem But I don't like this - I pretty much never do it
set test=abc^^^&xyz
rem Now this works
echo %test%
rem Better to single escape and use delayed expansion.
set test=abc^&xyz
rem This works because poison characters don't bother delayed expansion
echo !test!
rem You could use quotes instead of escape, but now quotes are in value
set test="abc&xyz"
rem Delayed expansion is not needed because value is quoted
echo %test%
rem Putting the leading quote before the variable still quotes everything
rem But now the quotes are not in the value, as desired. Value = abc&xyz
set "test=abc&xyz"
rem Now you must use delayed expansion because value is not quoted
echo !test!
So the general rules of thumb that I like to use when poison characters are involved:
Use quotes around the entire assignment when SETting a variable: set "var=value"
Use delayed expansion when expanding a variable: !var!
Wouldn't it be nice if those rules solved everything. But of course batch is not that simple. There are situations where these simple rules will fail, but it should be enough to get you started.