My goal / problem
The following batch program can be used to display my problem:
echo first param: %1 third param: %3
echo '%2'
my goal is to get the following output for param2 (I am aware that it would be a little easier as last parameter but that's not what is needed):
'<body><p style="font-familiy:Arial,Cascadia">MyText</p></body>'
My question is how to call it correctly or change the cmd-code so that the 2nd parameter is not interpreted as multiple parameters
First try: backslashes as escape character
if I escape the double quotes with a backslash like so:
demo p1 "<body><p style=\"font-family:Arial,Cascadia\">MyText</p></body>" p3
the output for param2 is
''"<p style="font-family:Arial'
and the rest of the HTML code is in param3.
Second try: double double quotes as escape character
My second attempt was to escape with double dobule quotes:
demo p1 "<body><p style=""font-family:Arial,Cascadia"">MyText</p></body>" p3
this produces the following output for param2 where the double quotes are not changed at all:
"'<body><p style=""font-family:Arial,Cascadia"">MyText</p></body>'"
The ugly workaround
I managed to get the desired output by replacing from "" to " like so:
set x=%2
echo '%x:""="%'
I wonder if this is the recommended way or if there is an easier way. I read a lot on this subject
with https://www.robvanderwoude.com/escapechars.php and http://www.windowsinspired.com/understanding-the-command-line-string-and-arguments-received-by-a-windows-program/ being cited most often but unfortunately neither did help.
As already mentioned, only double quotes can be used to escape a full argument, but the quotes itself can't be escaped inside an argument.
If you still insist on using double quotes in your arguments, the double double quote seems to be one of the best solutions.
But to get and handle the arguments in a safe way, you should switch to delayed expansion, because delayed expanded content isn't parsed anymore (contrary to percent expansion).
set "arg1=%~1"
set "arg2=%~2"
..
setlocal EnableDelayedExpansion
set "arg1=!arg1:""="!"
set "arg2=!arg2:""="!"
echo !arg1! !arg2!
Related
First time here, but dealing with batch codes from a long time.
I'd like to get some help developing a proper way to get correct parameters in a batch function.
In some functions, I usually need to get the parameters the way they are really passed, not "eating" any comma and not getting the script broked by any special characters.
If the function has one parameter only, it's easy and everything is there in %* the way it should.
But I usually I need to get some auxiliary paramters like variables or paths first and, so, I set the argument that might have some special stuff to the last position.
This is the only way I've found to use and treat complex arguments. But implementing this is always a trouble.
When I thought that I've found a best way by using FOR /F and tokens with *, I got problems when the first arguments (like paths) have spaces (or any other delimiter) because quotes are ignored then.
The only way I can achieve what I want by now is getting each numbered argument and its length then subtracting from all arguments, getting then the "rest argument" that I want, but that is some kind expensive, because I have to call String Len code many times.
Is there a way to use FOR /F delimiting by space but respecting quoted parameters with spaces?
Or anyone else knows a better and proper way of achieving what I want?!
Here's some code to illustrate the problem:
:: When no space in first arguments everything works
CALL :MyFunc arg1 argument2 c:\path\to\arg3 c:\path\toarg4 ,run, anything "here" even specials "&" like "|<>" and single" quotes
:: But spaces broke first arguments
CALL :MyFunc arg1 "argument 2" c:\path\to\arg3 "c:\spaced path\to arg4" ,run, anything "here" even specials "&" like "|<>" and single" quotes
GOTO :EOF
:MyFunc
SETLOCAL EnableDelayedExpansion
ECHO Checking if everything is there
ECHO %*
ECHO.
SET params=%*
ECHO Traditional way of parsing parameters works for first params:
ECHO 1:%1 2:%2 3:%3 4:%4
ECHO But how to get rest this way?
ECHO.
ECHO Trying to get through FOR /F and tokens with * to get rest
FOR /F "tokens=1,2,3,4*" %%a IN ("!params!") DO SET "arg1=%%~a" & SET "arg2=%%~b" & SET "arg3=%%c" & SET "arg4=%%~d" & SET "argrest=%%e"
ECHO It will work for non spaced first params but not for parameters with spaces quoted because delims is space
ECHO Parsing Desired Arguments With FOR:
ECHO 1:!arg1! 2:!arg2! 3:!arg3! 4:!arg4! Rest:!argrest!
ECHO.
GOTO :EOF
Result:
Checking if everything is there
arg1 argument2 c:\path\to\arg3 c:\path\toarg4 ,run, anything "here" even specials "&" like "|<>" and single" quotes
Traditional way of parsing parameters works for first params:
1:arg1 2:argument2 3:c:\path\to\arg3 4:c:\path\toarg4
But how to get rest this way?
Trying to get through FOR /F and tokens with * to get rest
It will work for non spaced first params but not for parameters with spaces quoted because delims is space
Parsing Desired Arguments With FOR:
1:arg1 2:argument2 3:c:\path\to\arg3 4:c:\path\toarg4 Rest:,run, anything "here" even specials "&" like "|<>" and single" quotes
Checking if everything is there
arg1 "argument 2" c:\path\to\arg3 "c:\spaced path\to arg4" ,run, anything "here" even specials "&" like "|<>" and single" quotes
Traditional way of parsing parameters works for first params:
1:arg1 2:"argument 2" 3:c:\path\to\arg3 4:"c:\spaced path\to arg4"
But how to get rest this way?
Trying to get through FOR /F and tokens with * to get rest
It will work for non spaced first params but not for parameters with spaces quoted because delims is space
Parsing Desired Arguments With FOR:
1:arg1 2:argument 3:2" 4:c:\path\to\arg3 Rest:"c:\spaced path\to arg4" ,run, anything "here" even specials "&" like "|<>" and single" quotes
Thank you so much!
The Problem
In a main batch file, values are pulled from a .txt file (and SET as values of variables within this batch file). These values may each contain % characters.
These are read from the .txt file with no issues. However, when a variable with a value containing a % character is passed to a second batch file, the second batch file interprets any % characters as a variable expansion. (Note: There is no control over the second batch file.)
Example
echo %PERCENTVARIABLE%
Output: I%LOVE%PERCENT%CHARACTERS%
When passed to a second file and then echo'ed, would (probably) become IPERCENT, as it interprets %LOVE% and %CHARACTERS% as unset variables.
Research
I found the syntax to find and replace elements within a string in a batch file, as I thought I could potentially replace a % character with %% in order to escape it. However I cannot get it to work.
The syntax is -
set string=This is my string to work with.
set string=%string:work=play%
echo %string%
Where the output would then be This is my string to play with..
Questions
Is it possible to escape % characters using the find and replace syntax
in a variable? (If not, is there another way?)
Is it advisable to do so? (Could using these escape characters cause any issue in the second batch file which (as mentioned above) we would have no control over?)
Is there another way to handle this issue, if the above is not possible?
There are no simple rules that can be applied in all situations.
There are a few issues that make working with string literals in parameters difficult:
Poison characters like &, |, etc. must be escaped or quoted. Escaping is difficult because it can be confusing as to how many times to escape. So the recommendation is to usually quote the string.
Token delimiters like <space>, <tab>, =, ; and , cannot be included in a parameter value unless it is quoted.
A CALL to a script will double any quoted % characters, and there is no way to prevent this. Executing a script without CALL will not double the % characters. But if a script calls another script and expects control to be returned, then CALL must be used.
So we have a catch-22: On the one hand, we want to quote parameters to protect against poison characters and spaces (token delimiters). But to protect percents we don't want to quote.
The only reliable method to reliably pass string literals without concern of value corruption is to pass them by reference via environment variables.
The value to be passed should be stored in an environment value. Quotes and/or escapes and/or percent doubling is used to get the necessary characters in the value, but it is very manageable.
The name of the variable is passed in as a parameter.
The script accesses the value via delayed expansion. For example, if the first parameter is the name of a variable containing the value, then it is accessed as !%1!. Delayed expansion must be enabled before that syntax can be used - simply issue setlocal enableDelayedExpansion.
The beauty of delayed expansion is you never have to worry about corruption of poison characters, spaces, or percents when the variable is expanded.
Here is an example that shows how the following string literal can be passed to a subroutine
"<%|,;^> This & that!" & the other thing! <%|,;^>
#echo off
setlocal enableDelayedExpansion
set "parm1="^<%%^|,;^^^^^> This ^& that^^!" & the other thing^! <%%|,;^^^>"
echo The value before CALL is !parm1!
call :test parm1
exit /b
:test
echo The value after CALL is !%1!
-- OUTPUT --
The value before CALL is "<%|,;^> This & that!" & the other thing! <%|,;^>
The value after CALL is "<%|,;^> This & that!" & the other thing! <%|,;^>
But you state that you have no control over the 2nd called script. So the above elegant solution won't work for you.
If you were to show the code of the 2nd script, and show exactly what value you were trying to pass, then I might be able to give a solution that would work in that isolated situation. But there are some values that simply cannot be passed unless delayed expansion is used with variable names. (Actually, another option is to put the value in a file and read the value from the file, but that also requires change to your 2nd script)
may be...?
input.txt
I%LOVE%PERCENT%CHARACTERS%
batch1.bat
#echo off
setlocal enableDelayedExpansion
set/P var=<input.txt
echo(In batch 1 var content: %var%
set "var=!var:%%=%%%%!"
call batch2.bat "%var%"
endlocal
exit/B
batch2.bat
#echo off
set "var=%~1"
echo(In batch 2 var content: %var%
exit/B
I need to run a command in a DOS batch file that contains a double colon AND set the output to a variable. Like this
set /a TDR = C:\InCharge\CONSOLE\smarts\bin\dmctl -s SSA-SAM invoke SM_System::SM-System nameToAddr %SM_OBJ_InstanceName%
I keep getting "Missing operator". I assume that is due to those double-colons. How do I escape these? I tried back-slashes but that didn't work. I've tried putting the whole command in double-quotes and that also didn't work.
I can run the command by itself, ie without the "set /a TDR" and the output is correct. But I need to use that output as the value of a variable hence the "set /a"
Normal output for dmctl is this
{ "10.28.112.74" }
I am using dmctl to get the ip address for the hostname. I figured once I got the output I could strip off the brackets and quotations, but I haven't figured out how to grab the output.
Thank you in advance.
Colons do not need to be escaped. See Batch files - Escape Characters for details on what characters need to be escaped, and how to escape them.
"Missing operator" is being returned because SET /A only works with arithmetic operations, so it is looking for an arithmetic operator.
To assign the output of a command to a variable, you have to use the FOR command, similar to the following:
for /f "delims=" %%i in ('C:\InCharge\CONSOLE\smarts\bin\dmctl -s SSA-SAM invoke SM_System::SM-System nameToAddr %SM_OBJ_InstanceName%') do set myresult=%%i
See Reading the output of a command into a batch file variable
To trim 3 characters from the beginning and end of a string:
set mystring=%mystring:~3,-3%
This will remove the curly braces, spaces, and quotation marks that delimit the IP address in the output.
I found this at DOS - String Manipulation.
I have this string, call it str, which may look like this XinfoX. i want a command, using batch, which replaces the 'X's with this symbol '|' (a pipe symbol). i have tried this, but the cmd keeps closing/crashing.
thanks in advance
use variable edit/replace see here for details
set str=XinfoX
echo %str:X=^|%
C:\>echo %str:X=^|%
|info|
Your command is crashing because the | character functions as the pipe operator. But you want to use it as a literal character instead. The character must either be escaped or quoted.
set str=XinfoY
:: using quotes
set "str=%str:X=|%"
:: using escape
set str=%str:Y=^|%
The situation can get complicated if your string contains quotes such that some of the string is quoted and some is not. The solution is to use delayed expansion.
setlocal enableDelayedExpansion
set str="Xinfo1X"info2X
set "str=!str:X=|!"
I have a batch file which moves files from one folder to another. The batch file is generated by another process.
Some of the files I need to move have the string "%20" in them:
move /y "\\myserver\myfolder\file%20name.txt" "\\myserver\otherfolder"
This fails as it tries to find a file with the name:
\\myserver\myfolder\file0name.txt
Is there any way to ignore %? I'm not able to alter the file generated to escape this, such as by doubling percent signs (%%), escaping with / or ^ (caret), etc.
You need to use %% in this case. Normally using a ^ (caret) would work, but for % signs you need to double up.
In the case of %%1 or %%i or echo.%%~dp1, because % indicates input either from a command or from a variable (when surrounded with %; %variable%)
To achieve what you need:
move /y "\\myserver\myfolder\file%%20name.txt" "\\myserver\otherfolder"
I hope this helps!
The question's title is very generic, which inevitably draws many readers looking for a generic solution.
By contrast, the OP's problem is exotic: needing to deal with an auto-generated batch file that is ill-formed and cannot be modified: % signs are not properly escaped in it.
The accepted answer provides a clever solution to the specific - and exotic - problem, but is bound to create confusion with respect to the generic question.
If we focus on the generic question:
How do you use % as a literal character in a batch file / on the command line?
Inside a batch file, always escape % as %%, whether in unquoted strings or not; the following yields My %USERNAME% is jdoe, for instance:
echo My %%USERNAME%% is %USERNAME%
echo "My %%USERNAME%% is %USERNAME%"
On the command line (interactively) - as well as when using the shell-invoking functions of scripting languages - the behavior fundamentally differs from that inside batch files: technically, % cannot be escaped there and there is no single workaround that works in all situations:
In unquoted strings, you can use the "^ name-disrupter" trick: for simplicity, place a ^ before every % char, but note that you're not technically escaping % that way (see below for more); e.g., the following again yields something like My %USERNAME% is jdoe:
echo My ^%USERNAME^% is %USERNAME%
In double-quoted strings, you cannot escape % at all, but there are workarounds:
You can use unquoted strings as above, which then requires you to additionally ^-escape all other shell metacharacters, which is cumbersome; these metacharacters are: <space> & | < > "
Alternatively, unless you're invoking a batch file, , you can individually double-quote % chars as part of a compound argument (most external programs and scripting engines parse a compound argument such as "%"USERNAME"%" as verbatim string %USERNAME%):
some_exe My "%"USERNAME"%" is %USERNAME%
From scripting languages, if you know you're calling a binary executable, you may be able to avoid the whole problem by forgoing the shell-invoking functions in favor of the "shell-free" variants, such as using execFileSync instead of execSync in Node.js.
Optional background information re command-line (interactive) use:
Tip of the hat to jeb for his help with this section.
On the command line (interactively), % can technically not be escaped at all; while ^ is generally cmd.exe's escape character, it does not apply to %.
As stated, there is no solution for double-quoted strings, but there are workarounds for unquoted strings:
The reason that "^ name-disrupter" trick (something like ^%USERNAME^%) works is:
It "disrupts" the variable name; that is, in the example above cmd.exe looks for a variable named USERNAME^, which (hopefully) doesn't exist.
On the command line - unlike in batch files - references to undefined variables are retained as-is.
Technically, a single ^ inside the variable name - anywhere inside it, as long as it's not next to another ^ - is sufficient, so that %USERNAME^%, for instance, would be sufficient, but I suggest adopting the convention of methodically placing ^ before each and every % for simplicity, because it also works for cases such as up 20^%, where the disruption isn't even necessary, but is benign, so you can apply it methodically, without having to think about the specifics of the input string.
A ^ before an opening %, while not necessary, is benign, because ^ escapes the very next character, whether that character needs escaping - or, in this case, can be escaped - or not. The net effect is that such ^ instances are ultimately removed from unquoted strings.
Largely hypothetical caveat: ^ is actually a legal character in variable names (see jeb's example in the comments); if your variable name ends with ^, simply place the "disruptive" ^ somewhere else in the variable name, as long as it's not directly next to another ^ (as that would cause a ^ to appear in the resulting string).
That said, in the (very unlikely) event that your variable has a name such as ^b^, you're out of luck.
In batch files, the percent sign may be "escaped" by using a double percent sign ( %% ).
That way, a single percent sign will be used within the command line. from http://www.robvanderwoude.com/escapechars.php
I think I've got a partial solution working. If you're only looking to transfer files that have the "%20" string in their name and not looking for a broader solution, you can make a second batch file call the first with %%2 as the second parameter. This way, when your program tries to fetch the second parameter when it hits the %2 in the text name, it will replace the %2 with an escaped %2, leaving the file name unchanged.
Hope this works!
How to "escape" inside a batch file withoput modify the file**
The original question is about a generated file, that can't be modified, but contains lines like:
move /y "\\myserver\myfolder\file%20name.txt" "\\myserver\otherfolder"
That can be partly solved by calling the script with proper arguments (%1, %2, ...)
#echo off
set "per=%%"
call generated_file.bat %%per%%1 %%per%%2 %%per%%3 %%per%%4
This simply sets the arguments to:
arg1="%1"
arg2="%2"
...
How to add a literal percent sign on the command line
mklement0 describes the problem, that escaping the percent sign on the command line is tricky, and inside quotes it seems to be impossible.
But as always it can be solved with a little trick.
for %Q in ("%") do echo "file%~Q20name.txt"
%Q contains "%" and %~Q expands to only %, independent of quotes.
Or to avoid the %~ use
for /F %Q in ("%") do echo "file%Q20name.txt"
You should be able to use a caret (^) to escape a percent sign.
Editor's note: The link is dead now; either way: It is % itself that escapes %, but only in batch files, not at the command prompt; ^ never escapes %, but at the command prompt it can be used indirectly to prevent variable expansion, in unquoted strings only.
The reason %2 is disappearing is that the batch file is substituting the second argument passed in, and your seem to not have a second argument. One way to work around that would be to actually try foo.bat ^%1 ^%2... so that when a %2 is encountered in a command, it is actually substituted with a literal %2.