GeezerVison is showing github.com — Arguments for external executables aren't correctly escaped · Issue #1995 · PowerShell/PowerShell
Arguments for external executables aren't correctly escaped #1995Description
Steps to reproduce
- write a C program
native.exe which acquires ARGV
- Run
native.exe "`"a`""
Expected behavior
ARGV[1] == "a"
Actual behavior
ARGV[1] == a
Environment data
Windows 10 x64
Name Value
---- -----
PSVersion 5.1.14393.0
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.14393.0
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
Reacted by ggirard07, Nate McMaster, Amar Sood, Daniel Fallon, Giorgi Chakhidze, Mariusz Pawelski, Alexandre L., Nathan Lowe, Francis Agyapong, Felix Becker and 27 more Activityandyleejordan commented on Aug 31, 2016 Why do you think that "\"a\"" the expected behavior? My understanding of PowerShell escapes says that the actual behavior is the correct and expected behavior. ""a"" is a pair of quotes surrounding an escaped pair of quotes surrounding an a, so PowerShell interprets the outer unescaped pair as "this is a string argument" and so drops them, then interprets the escaped pair as escaped quotes and so keeps them, leaving you with "a". At no point was a \ added to the string.
The fact that Bash uses \ as an escape character is irrelevant. In PowerShell, the escape character is a backtick. See PowerShell escape characters.
If you want to pass literally "\"a\"", I believe you would use:
> echo `"\`"a\`"`"
"\"a\"" be5invis commented on Aug 31, 2016 Andy Jordan (@andschwa)
Yes, escapes works fine for internal cmdlets, but things get weird when communicate with native binaries, especially on Windows.
When running native.exe ""a"", the ARGV[1] should be
(three characters)
instead of
(one character). be5invis commented on Aug 31, 2016 Currently to make native.exe correctly receive an ARGV with two quotes and an a character, you have to use this weird call:
Reacted by Michael Klement, Ivan Akcheurov, Doug Maurer and Serhan Apaydın Reacted by Nathan Lowe, Gennadiy Belousov, Jiashuo Li, Amin Ya, Ivan Akcheurov, Luiz, oczkoisse, Haoran Ni and Lars Moelleken andyleejordan commented on Aug 31, 2016 andyleejordan commented on Aug 31, 2016 Out of a strong curiosity, what happens if you try a build using #1639? be5invis commented on Aug 31, 2016 Andy Jordan (@andschwa) The same. You HAVE to double-esacpe to satisify both PowerShell and CommandLineToArgvW. This line:
results a StartProcess equalivent to cmd
andyleejordan commented on Sep 19, 2016 be5invis commented on Sep 20, 2016 No, We still need to add a backslash before a backtick-escaped double quote? This does not solve the double-escaping problem. (That is, we have to escape a double quote for both PowerShell and CommandLineToArgvW.) vors commented on Sep 20, 2016 Since ""a"" is equal to '"a"', do you suggest that native.exe '"a"' should result in "\"a\""? douglaswth commented on Sep 20, 2016 This seems like a feature request that if implemented could break a large number of already existing PowerShell scripts that use the required double escaping, so extreme care would be required with any solution. be5invis commented on Sep 20, 2016 Reacted by Michael Klement, Jiashuo Li, Roy Ivy III and Paul Abrams be5invis commented on Sep 20, 2016 sergei (@vors) Douglas Thrift (@douglaswth)
This is a the C code used to show GetCommandLineW and CommandLineToArgvW results:
#include <stdio.h>
#include <wchar.h>
#include <Windows.h>
int main() {
LPWSTR cmdline = GetCommandLineW();
wprintf(L"Command Line : %s\n", cmdline);
int nArgs;
LPWSTR *szArglist = CommandLineToArgvW(cmdline, &nArgs);
if (NULL == szArglist) {
wprintf(L"CommandLineToArgvW failed\n");
return 0;
} else {
for (int i = 0; i < nArgs; i++) {
wprintf(L"argv[%d]: %s\n", i, szArglist[i]);
}
}
LocalFree(szArglist);
}be5invis commented on Sep 20, 2016 Here is the result
$ ./a "a b"
Command Line : "Z:\playground\ps-cmdline\a.exe" "a b"
argv[0]: Z:\playground\ps-cmdline\a.exe
argv[1]: a b
$ ./a 'a b'
Command Line : "Z:\playground\ps-cmdline\a.exe" "a b"
argv[0]: Z:\playground\ps-cmdline\a.exe
argv[1]: a b
$ ./a 'a"b'
Command Line : "Z:\playground\ps-cmdline\a.exe" a"b
argv[0]: Z:\playground\ps-cmdline\a.exe
argv[1]: ab
$ ./a 'a"b"c'
Command Line : "Z:\playground\ps-cmdline\a.exe" a"b"c
argv[0]: Z:\playground\ps-cmdline\a.exe
argv[1]: abc
$ ./a 'a\"b\"c'
Command Line : "Z:\playground\ps-cmdline\a.exe" a\"b\"c
argv[0]: Z:\playground\ps-cmdline\a.exe
argv[1]: a"b"c
Reacted by Jiashuo Li, Tatsunori Uchino, bb010g and Giorgi Chakhidze mklement0 commented on May 6, 2021 A decision regarding the accommodations on Windows has been made: #15143 (comment). SteveL-MSFT commented on Aug 11, 2022 This appears to be fixed in 7.3:
PS> testexe -echoargs "`"a`""
Arg 0 is <"a"> jiasli commented on Aug 11, 2022 jiasli commented on Aug 11, 2022 I can verify double quotes are escaped correctly in v7.3.0-preview.6 when calling a .exe.
> echo $PSVersionTable
Name Value
---- -----
PSVersion 7.3.0-preview.6
...
> python -c "import sys; print(sys.argv)" '"ab"'
['-c', '"ab"']
However, special characters (", |, &) are still not escaped correctly when invoking a cmd script. Consider a very simply script test.cmd:
echo %*
python -c "import sys; print(sys.argv)" %*
For |, running test.cmd in PowerShell v7.3.0-preview.6 gives:
PS D:\> ./test.cmd "a|b"
'b' is not recognized as an internal or external command,
operable program or batch file.
This differs from the behavior of Command Prompt:
D:\>test.cmd "a|b"
D:\>echo "a|b"
"a|b"
D:\>python -c "import sys; print(sys.argv)" "a|b"
['-c', 'a|b']
This causes problem for Azure CLI when there is | or & in argument value: Azure/azure-cli#20972
For ", running test.cmd in PowerShell v7.3.0-preview.6 gives:
PS D:\> ./test.cmd "`"ab`""
D:\>echo "ab"
"ab"
D:\>python -c "import sys; print(sys.argv)" "ab"
['-c', 'ab']
This differs from the behavior of Command Prompt:
D:\>test.cmd "\"ab\""
D:\>echo "\"ab\""
"\"ab\""
D:\>python -c "import sys; print(sys.argv)" "\"ab\""
['-c', '"ab"']
rivy commented on Aug 11, 2022 Reacted by Christopher Yeleighton brendandburns commented on Aug 11, 2022 The PowerShell behavior here is the same as bash
test.sh:
bburns@DESKTOP-VHJJ57D:~$ ./test.sh "a|b"
a|b
bburns@DESKTOP-VHJJ57D:~$ ./test.sh "a&b"
a&b
bburns@DESKTOP-VHJJ57D:~$
In my opinion, the bash/powershell behavior makes more sense than the cmd behavior. In the above example, the quotes are intended for the shell interpreter, they are not part of the user's input. The shell's job is to pass each value as a String to the command that it calls. It is the executable/command scripts responsibility to re-escape as necessary before shelling out again. brendandburns commented on Aug 11, 2022 Furthermore this seems to be a specific behavior related to cmd.exe calling command scripts.
import sys
print(sys.argv)
C:\Users\bburns>py test.py 'a|b'
'b'' is not recognized as an internal or external command,
operable program or batch file.
C:\Users\bburns>py test.py 'a&b'
['test.py', "'a"]
'b'' is not recognized as an internal or external command,
operable program or batch file. yecril71pl commented on Sep 16, 2022
As noted by Jiashuo Li (@jiasli), this issue is not actually fixed.
This issue is about passing arguments to native executables that support passing arguments. The executable cmd.exe is not one of them, it uses the whole command line instead. The only reliable way to call cmd.exe is to print the expected command line out into a temporary command script and run that script without any arguments.
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|