Opening local (file) URL with parameters in batch file? [duplicate] - windows

How do I escape ampersands in a batch file (or from the
Windows command line) in order to use the start command to
open web pages with ampersands in the URL?
Double quotes will not work with start; this starts a new
command-line window instead.
Update 1: Wael Dalloul's solution works. In addition, if
there are URL encoded characters (e.g. space is encoded as
%20) in the URL and it is in a batch file then '%' must be
encoded as '%%'. This is not the case in the example.
Example, from the command line (CMD.EXE):
start http://www.google.com/search?client=opera&rls=en&q=escape+ampersand&sourceid=opera&ie=utf-8&oe=utf-8
will result in
http://www.google.com/search?client=opera
being opened in the default browser and these errors in the command line window:
'rls' is not recognized as an internal or external command,
operable program or batch file.
'q' is not recognized as an internal or external command,
operable program or batch file.
'sourceid' is not recognized as an internal or external command,
operable program or batch file.
'ie' is not recognized as an internal or external command,
operable program or batch file.
'oe' is not recognized as an internal or external command,
operable program or batch file.
Platform: Windows XP 64 bit SP2.

& is used to separate commands. Therefore you can use ^ to escape the &.

From a cmd:
& is escaped like this: ^& (based on #Wael Dalloul's answer)
% does not need to be escaped
An example:
start http://www.google.com/search?client=opera^&rls=en^&q=escape+ampersand%20and%20percentage+in+cmd^&sourceid=opera^&ie=utf-8^&oe=utf-8
From a batch file
& is escaped like this: ^& (based on #Wael Dalloul's answer)
% is escaped like this: %% (based on the OPs update)
An example:
start http://www.google.com/search?client=opera^&rls=en^&q=escape+ampersand%%20and%%20percentage+in+batch+file^&sourceid=opera^&ie=utf-8^&oe=utf-8

You can enclose it in quotes, if you supply a dummy first argument.
Note that you need to supply a dummy first argument in this case, as start will treat the first argument as a title for the new console windows, if it is quoted. So the following should work (and does here):
start "" "http://www.google.com/search?client=opera&rls=en&q=escape+ampersand&sourceid=opera&ie=utf-8&oe=utf-8"

explorer "http://www.google.com/search?client=opera&rls=...."

The command
echo this ^& that
works as expected, outputing
this & that
The command
echo this ^& that > tmp
also works, writing the string to file "tmp". However, before a pipe
echo this ^& that | clip
the ^ is interpreted completely differently. It tries to write the output of the two commands "echo this" and "that" to the pipe. The echo will work then "that" will give an error. Saying
echo this ^& echo that | clip
will put the strings "this" and "that" on the clipboard.
Without the ^:
echo this & echo that | clip
the first echo will write to the console and only the second echo's output will be piped to clip (similarly for "> tmp" redirection). So, when output is being redirected, the ^ does not quote the & but instead causes it to be applied before the redirection rather than after.
To pipe an &, you have to quote it twice
echo this ^^^& that | clip
If you put the string in a variable
set m=this ^& that
then
set m
will output
m=this & that
but the obvious
echo %m%
fails because, after Windows substitutes the variable, resulting in
echo this & that
it parses this as a new command and tries to execute "that".
In a batch file, you can use delayed expansion:
setlocal enableDelayedExpansion
echo !m!
To output to a pipe, we have to replace all &s in the variable value with ^&, which we can do with the %VAR:FROM=TO% syntax:
echo !m:^&=^^^&! | clip
On the command line, "cmd /v" enables delayed expansion:
cmd /v /c echo !m!
This works even when writing to a pipe
cmd /v /c echo !m! | clip
Simple.

If you need to echo a string that contains an ampersand, quotes won't help, because you would see them on the output as well. In such a case, use for:
for %a in ("First & Last") do echo %~a
...in a batch script:
for %%a in ("First & Last") do echo %%~a
or
for %%a in ("%~1") do echo %%~a

If you have spaces in the name of the file and you have a character you need to escape:
You can use single AND double quotes to avoid any misnomers in the command.
scp ./'files name with spaces/internal folder with spaces/"text & files stored.txt"' .
The ^ character escapes the quotes otherwise.

For special characters like '&' you can surround the entire expression with quotation marks
set "url=https://url?retry=true&w=majority"

Related

weird handling of double quotes when using cmd /c "echo"

I'm trying to escape some calls in bat file and I found out that I don't understand even on simple example the weird handling of double quotes:
Try
cmd /c "echo " "%TEMP%" rem this gives: " "C:\Users\xyz\AppData\Local\Temp
cmd /c "echo" "%TEMP%" rem this gives: The filename, directory name, or volume label syntax is incorrect.
cmd /c "echo beg "TEMP" %TEMP%" rem this gives: beg "TEMP" C:\Users\xyz\AppData\Local\Temp
cmd /c "echo beg ^"TEMP^" %TEMP%" rem this gives: beg "TEMP" C:\Users\xyz\AppData\Local\Temp
cmd /c "echo beg \"TEMP\" %TEMP%" rem this gives: beg \"TEMP\" C:\Users\xyz\AppData\Local\Temp
Just open command prompt (cmd.exe) and paste code.
The results are the same when copy/paste to cmd.exe or running a bat file.
What I would expect is that cmd /c "echo beg \"TEMP\" %TEMP%" should work correcctly. At least according to http://daviddeley.com/autohotkey/parameters/parameters.htm#WIN .
But I don't understand output from all the samples. Anybody could explain me that behaviour?
Edit:
The site I'm referencing just explains how arguments are parsed on Windows. E.g. http://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULES
What is my expected output?
I just want to know how that passing of double quotes work.
Later I'd like to construct command lines like this one:
pwsh -noprofile -command "produceSomeStringsToFind | % { rg -g testfile* \" $_ some string that ends with quote\\\"" . }"
where rg is https://github.com/BurntSushi/ripgrep - where the \" $_ some string that ends with quote\\\"" part is regular expression where I need to escape the quotes.
From the help text of cmd /?:
[...]
If /C or /K is specified, then the remainder of the command line after
the switch is processed as a command line, where the following logic is
used to process quote (") characters:
1. If all of the following conditions are met, then quote characters
on the command line are preserved:
- no /S switch
- exactly two quote characters
- no special characters between the two quote characters,
where special is one of: &<>()#^|
- there are one or more whitespace characters between the
two quote characters
- the string between the two quote characters is the name
of an executable file.
2. Otherwise, old behavior is to see if the first character is
a quote character and if so, strip the leading character and
remove the last quote character on the command line, preserving
any text after the last quote character.
[...]
It becomes clear that you are facing the situation explained in section 2., because echo is not an executable file but an internal command. So if the first character is a ", it becomes removed and so becomes the last ", and all the other characters are preserved.
Now let us go through your command lines. So after being parsed and processed by cmd /c:
cmd /c "echo " "%TEMP%" becomes echo " "C:\Users\xyz\AppData\Local\Temp.
cmd /c "echo" "%TEMP%" becomes echo" "C:\Users\xyz\AppData\Local\Temp; then the command echo" is tried to be executed, but which is not a valid one, hence it fails.
cmd /c "echo beg "TEMP" %TEMP%" becomes echo beg "TEMP" C:\Users\xyz\AppData\Local\Temp.
cmd /c "echo beg ^"TEMP^" %TEMP%" becomes echo beg "TEMP" C:\Users\xyz\AppData\Local\Temp, because escaping with ^ happens even before cmd /c is executed, so it receives the already escaped literal " characters.
cmd /c "echo beg \"TEMP\" %TEMP%" becomes echo beg \"TEMP\" C:\Users\xyz\AppData\Local\Temp, because the \ is nothing special to cmd.
If you want to output the text "%TEMP%" (including the quotes), you could do this:
rem // This preserves all quotes, because the first character is not such:
cmd /c echo "%TEMP%"
rem // This removes the outer-most pair of quotes, because the first character is such:
cmd /c "echo "%TEMP%""
rem /* This removes the outer-most pair of quotes too, since `cmd /c` receives the already
rem escaped `"`; however, this could be useful to hide these quotes from the hosting
rem `cmd` instance, which avoids issues when `%TEMP%` contains special characters
rem (like `&` or `^`), which you would otherwise have to individually escape: */
cmd /c ^"echo "%TEMP%"^"

including special char (&) from "for delims"-result in batch file

I have a script that reads thru file and sets variable as it finds it.
#echo off
Setlocal EnableDelayedExpansion
for /f "tokens=*" %%V in ('findstr /I /C:title= "%~1"') do set title=%%V
echo %title%
In the txt file there is "title=variable & speed".
And the script only returns:
title=variable
'SPEED' is not recognized as an internal or external command,
operable program or batch file.
As it should return whole line.
This is the part, I have not found the solution yet. It should change the "&" to "-", as in finally this script renames files.
First, don't enable delayed expansion because of not needed here. It can result in findstr does not find the file to open if the batch file is called with a file name without or with a path containing one or more exclamation marks.
Second, the FOR option "tokens=*" results in removing leading spaces/tabs from a line output by FINDSTR not starting with a semicolon and if there is something left assign the rest of the line to specified loop variable. This is okay if this behavior is wanted here. Otherwise it would be better to use "delims=" which defines an empty list of delimiters resulting in assigning the entire line not starting with a semicolon to the specified loop variable. Not double quoted argument string delims^=^ eol^= defines an empty list of delimiters and no end of line character to get assigned also a line starting with a semicolon to the loop variable. The two equal signs and the space character must be escaped with caret character ^ to be interpreted as literal characters and not as argument separators.
Third, an ampersand outside a double quoted argument string is interpreted as operator to unconditionally execute the command after & after executing the command before &. For details see Single line with multiple commands using Windows batch file. For that reason it is recommended to enclose the argument string of command SET in double quotes as explained in detail on answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line?
So I suggest using following code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LoadedTitle="
for /F "tokens=*" %%V in ('%SystemRoot%\System32\findstr.exe /I /C:"title=" "%~1" 2^>nul') do set "LoadedTitle=%%V"
if defined LoadedTitle (
setlocal EnableDelayedExpansion
echo !LoadedTitle!
endlocal
)
endlocal
Read this answer for details about the commands SETLOCAL and ENDLOCAL.
Please note that /C:"title=" is used instead of /C:title= on FINDSTR command line as otherwise FINDSTR would in this special case search just for title. The reason is that the command line within the round brackets is executed in a separate command process started by FOR with cmd.exe /C in background and the equal sign not enclosed in a double quoted string and not escaped with ^ would be removed by current command process because of being interpreted as separator. In a command prompt window it is possible to use the FINDSTR command line with /C:title= without double quotes, but not here on this FOR command line in batch file.
Read also the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded findstr command line with using a separate command process started in background.
Temporary enabling delayed expansion just for output of the line with loaded title string is required because of usage of only echo %LoadedTitle% would be modified before execution to echo title=variable & speed and the ampersand is again not interpreted as literal character to output by ECHO, but as operator to run speed after execution of echo title=variable .
I recommend to read
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
How to debug a batch file?
A batch file writer must always take into account what is finally executed by Windows command processor after parsing a command line one or more times as this can be different than what is written in batch file on using environment variable references with syntax %variable%.
I Found the correct "formula" to script
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /F "tokens=*" %%V in ('%SystemRoot%\System32\findstr.exe /I /C:"title=" "%~1"
2^>nul') do set "title=%%V"
set title=!title:^&=-!
echo "!title!"
endlocal
As it does now that what I wanted, it returns:
"title=variable - SPEED"
It is not as you suggested but it does the job.

How to pass a command with spaces and quotes as a single parameter to CScript?

I'm using CScript to run a VBScript file, and I need to pass a command line to the script where the parameter includes both spaces and quotes. The entire command needs to be passed as one parameter to the script. For example:
C:\> CScript myscript.vbs //Nologo "cmd.exe /c "dir && del /q *.txt""
Note the specific command line above is just an example, but it does touch on what I'm trying to do (i.e. pass a command line that involves running cmd.exe). I'm trying to solve for the general case, however.
The issue is that CScript appears to be getting confused trying to determine where quotes are opening and closing. At least sometimes.
The following script may help explain. It just outputs the passed in arguments as it sees them:
Dim count
For count = 0 To WScript.Arguments.Count-1
WScript.Echo CStr(count) & " = " & WScript.Arguments.Item(count)
Next
Often times, it works as expected:
C:\> cscript myscript.vbs //Nologo "1 2 3"
0 = 1 2 3
But when there are quotes inside the quoted parameter, things change:
C:\> cscript myscript.vbs //Nologo "1 "2 3""
0 = 1 2
1 = 3
I understand there is an introduced ambiguity about whether each quote is opening or closing a parameter in the above example, so next I tried escaping the internal quotes so the command processor will treat them as literal strings:
C:\> cscript myscript.vbs //Nologo "1 ^"2 3^""
0 = 1 ^2
1 = 3
Hmmm, looks like that only works when the shell does your globbing and such for you. I doubt cscript understands ^ as an escape character like cmd.exe does.
Using single quotes inside the double-quoted command (e.g. "cmd.exe /c 'dir && cd \'") properly parses as a single parameter but won't work because for the case of invoking cmd.exe with the /c parameter, single quotes are not sufficient. For example:
C:\> cmd /c 'dir && cd \'
''dir' is not recognized as an internal or external command,
operable program or batch file.
I suppose I could just iterate over all the arguments in the script and join them back to one long string, but I'll lose the internal quotes and context that they provide doing that. Or maybe I do use single quotes inside the the double-quoted param and map those back to double quotes inside the script. But what if the intention was single quotes for some future case I'm not considering? Since I'm attempting to solve for the general case, I can't make that determination.
Any ideas?
It appears like you are trying to delineate values in your parameters. I suggest using something besides quotes and adding back the quotes. For example using tildes:
Dim count
For count = 0 To WScript.Arguments.Count-1
WScript.Echo CStr(count) & " = " & replace(WScript.Arguments.Item(count),"~",chr(34))
Next
Passing:
cscript myscript.vbs //Nologo "1 ~2 3~"
0 = 1 "2 3"
You have two options.
The first is easy enough--escape the quotes using the carat (^).
C:\> CScript myscript.vbs //Nologo "cmd.exe /c ^"dir && del /q *.txt^""
The second option is to use a different delimiter. In a command like this, the shell uses a space as a delimeter between parameters. But the shell allows others like semicolons, commas, equal signs, and tabs. By implementing one of these, you wouldn't need to surround parameters with quotes because spaces would no longer break your command line.

run two commands in one windows cmd line, one command is SET command

[purpose]
This simple command sequence runs expected in the Windows' CMD shell:
dir & echo hello
will list the files and directories and echo the string.
However, the following command sequence does not run as expected (at least by me):
C:\Users\Administrator>set name=value & echo %name%
%name%
C:\Users\Administrator>echo %name%
value
C:\Users\Administrator>
As we can see, the first echo cannot get the environment. Could you help to comment? Any comment will be appreciated!
PS: OS:Windows 7 X64 Home Pre
Your result is due to the fact that %name% is expanded during the parsing phase, and the entire line is parsed at once, prior to the value being set.
You can get the current value on the same line as the set command in one of two ways.
1) use CALL to cause ECHO %NAME% to be parsed a 2nd time:
set name=value&call echo %^name%
I put a ^ between the percents just in case name was already defined before the line is executed. Without the caret, you would get the old value.
Note: your original line had a space before the &, this space would be included in the value of the variable. You can prevent the extra space by using quotes: set "name=value" &...
2) use delayed expansion to get the value at execution time instead of at parse time. Most environments do not have delayed expansion enabled by default. You can enable delayed expansion on the command line by using the appropriate CMD.EXE option.
cmd /v:on
set "name=value" & echo !name!
Delayed expansion certainly can be used on the command line, but it is more frequently used within a batch file. SETLOCAL is used to enable delayed expansion within a batch file (it does not work from the command line)
setlocal enableDelayedExpansion
set "name=value" & echo !name!
You can also use cmd /V /C (with /V to enable delayed expansion).
That is great to set an environment variable for just one command in Windows cmd.exe:
cmd /V /C "set "name=value" && echo !name!"
value
Note the usage of double-quotes in set "name=value" to avoid the extra space after value.
For instance, without double-quotes:
cmd /V /C "set name=value && echo '!name!'"
'value '
You would need to think to remove the space between value and &&:
cmd /V /C "set name=value&& echo '!name!'"
'value'
But using double-quotes makes the assignment more explicit.

How do I escape ampersands in batch files?

How do I escape ampersands in a batch file (or from the
Windows command line) in order to use the start command to
open web pages with ampersands in the URL?
Double quotes will not work with start; this starts a new
command-line window instead.
Update 1: Wael Dalloul's solution works. In addition, if
there are URL encoded characters (e.g. space is encoded as
%20) in the URL and it is in a batch file then '%' must be
encoded as '%%'. This is not the case in the example.
Example, from the command line (CMD.EXE):
start http://www.google.com/search?client=opera&rls=en&q=escape+ampersand&sourceid=opera&ie=utf-8&oe=utf-8
will result in
http://www.google.com/search?client=opera
being opened in the default browser and these errors in the command line window:
'rls' is not recognized as an internal or external command,
operable program or batch file.
'q' is not recognized as an internal or external command,
operable program or batch file.
'sourceid' is not recognized as an internal or external command,
operable program or batch file.
'ie' is not recognized as an internal or external command,
operable program or batch file.
'oe' is not recognized as an internal or external command,
operable program or batch file.
Platform: Windows XP 64 bit SP2.
& is used to separate commands. Therefore you can use ^ to escape the &.
From a cmd:
& is escaped like this: ^& (based on #Wael Dalloul's answer)
% does not need to be escaped
An example:
start http://www.google.com/search?client=opera^&rls=en^&q=escape+ampersand%20and%20percentage+in+cmd^&sourceid=opera^&ie=utf-8^&oe=utf-8
From a batch file
& is escaped like this: ^& (based on #Wael Dalloul's answer)
% is escaped like this: %% (based on the OPs update)
An example:
start http://www.google.com/search?client=opera^&rls=en^&q=escape+ampersand%%20and%%20percentage+in+batch+file^&sourceid=opera^&ie=utf-8^&oe=utf-8
You can enclose it in quotes, if you supply a dummy first argument.
Note that you need to supply a dummy first argument in this case, as start will treat the first argument as a title for the new console windows, if it is quoted. So the following should work (and does here):
start "" "http://www.google.com/search?client=opera&rls=en&q=escape+ampersand&sourceid=opera&ie=utf-8&oe=utf-8"
explorer "http://www.google.com/search?client=opera&rls=...."
The command
echo this ^& that
works as expected, outputing
this & that
The command
echo this ^& that > tmp
also works, writing the string to file "tmp". However, before a pipe
echo this ^& that | clip
the ^ is interpreted completely differently. It tries to write the output of the two commands "echo this" and "that" to the pipe. The echo will work then "that" will give an error. Saying
echo this ^& echo that | clip
will put the strings "this" and "that" on the clipboard.
Without the ^:
echo this & echo that | clip
the first echo will write to the console and only the second echo's output will be piped to clip (similarly for "> tmp" redirection). So, when output is being redirected, the ^ does not quote the & but instead causes it to be applied before the redirection rather than after.
To pipe an &, you have to quote it twice
echo this ^^^& that | clip
If you put the string in a variable
set m=this ^& that
then
set m
will output
m=this & that
but the obvious
echo %m%
fails because, after Windows substitutes the variable, resulting in
echo this & that
it parses this as a new command and tries to execute "that".
In a batch file, you can use delayed expansion:
setlocal enableDelayedExpansion
echo !m!
To output to a pipe, we have to replace all &s in the variable value with ^&, which we can do with the %VAR:FROM=TO% syntax:
echo !m:^&=^^^&! | clip
On the command line, "cmd /v" enables delayed expansion:
cmd /v /c echo !m!
This works even when writing to a pipe
cmd /v /c echo !m! | clip
Simple.
If you need to echo a string that contains an ampersand, quotes won't help, because you would see them on the output as well. In such a case, use for:
for %a in ("First & Last") do echo %~a
...in a batch script:
for %%a in ("First & Last") do echo %%~a
or
for %%a in ("%~1") do echo %%~a
If you have spaces in the name of the file and you have a character you need to escape:
You can use single AND double quotes to avoid any misnomers in the command.
scp ./'files name with spaces/internal folder with spaces/"text & files stored.txt"' .
The ^ character escapes the quotes otherwise.
For special characters like '&' you can surround the entire expression with quotation marks
set "url=https://url?retry=true&w=majority"

Resources