以下代码打印hi
:
import subprocess
subprocess.call(['sh', '-c', ' "$@" ', '-', 'echo', 'hi'])
但是,在 native Windows Python 上,对代码稍作修改:
import subprocess
subprocess.call(['sh', '-c', '"$@"' , '-', 'echo', 'hi'])
产生以下错误:
sh: -c: line 0: unexpected EOF while looking for matching `"'
sh: -c: line 1: syntax error: unexpected end of file
这是为什么?
最佳答案
这似乎是 MSYS2(可能来自 Cygwin)中的一个令人惊讶的长期存在的错误,由 Windows 的 idiosyncratic 引起。 quoting rules . 发生的事情是 MSYS2 期望的
subprocess.list2cmdline(['sh', '-c', '"$@"' , '-', 'echo', 'hi'])
翻译成
sh -c "\"$@\"" - echo hi
但实际上它会产生以下内容:
sh -c \"$@\" - echo hi
很难理解为什么会这样,直到您意识到 MSYS2 认为 Windows 命令行引用规则是这样的,即反斜杠被视为双引号之外的文字。
所以最终发生的是 \"$@\"
被解释为单个文字反斜杠,后跟带引号的字符串 $@\"
,其结束引号是 < strong>缺失。如果我们添加结束引号,它实际上看起来像 \"$@\""
,似乎不平衡,但在事实上平衡到 MSYS2。 (!)
然而,当参数包含空格时,整件事都会被天真地引用,意外地掩盖了问题。
为什么它会这样解释事情?这可能是因为文档,says :
CommandLineToArgvW
has a special interpretation of backslash characters when they are followed by a quotation mark character ("
). This interpretation assumes that any preceding argument is a valid file system path, or else it may behave unpredictably.This special interpretation controls the "in quotes" mode tracked by the parser.
当解析器不处于“in-quotes”模式时,很容易误读它并认为反斜杠失去了它们的特殊含义,这就是 MSYS2 解析器所说的。但是,如果您仔细阅读接下来的两个句子,它会解释“in-quotes”模式的确切含义:
When this mode is off, whitespace terminates the current argument. When on, whitespace is added to the argument like all other characters.
这就是全部。反斜杠 don't 突然变成逐字外引号。他们仍然可以像在内部一样转义引号,当然除了规则在内部比外部更复杂。
您如何解决这个问题?值得庆幸的是, native Windows Python 允许将整个命令行作为单个字符串文字传递,因此您实际上可以使用辅助方法解决此错误:
import subprocess
def list2cmdline(args): return ' '.join(map(
lambda a: a if a.lstrip().startswith('"') or '"' not in a else '"' + a + '"',
map(lambda a: subprocess.list2cmdline([a]), args)))
subprocess.call(list2cmdline(['sh', '-c', '"$@"', '-', 'echo', 'hi']))
或者,您可以直接对其进行 monkeypatch:
import subprocess
subprocess.list2cmdline = (lambda old: lambda args: ' '.join(map(
lambda a: a if a.lstrip().startswith('"') or '"' not in a else '"' + a + '"',
map(lambda a: old([a]), args))))(subprocess.list2cmdline)
subprocess.call(['sh', '-c', '"$@"', '-', 'echo', 'hi'])
这应该不会影响任何正常运行的程序,因为有多种引用方式,但它应该可以解决 MSYS2 的问题。
关于python - 为什么 Python 的子进程调用在 Windows 上不能与 sh 一起正常工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58687686/