python - 为什么 Python 的子进程调用在 Windows 上不能与 sh 一起正常工作?

标签 python windows escaping

以下代码打印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/

相关文章:

python - 区分nginx、haproxy、varnish和uWSGI/Gunicorn

c++ - 如何异步刷新内存映射文件?

c# - 如何使用预先保存的 VPN 凭据启动 VPN?

javascript - 通过 JavaScript 动态注入(inject)为 CSS 转义的 HTML 实体

Linux 删除转义字符

python - Apache2&Django - NameError : name "AttributeError" is not defined

python - 如何将悬停突出显示添加到 Bokeh 步骤图

python - Matplotlib 在附加轴上的错误位置打勾

windows - 在哪里可以找到详尽的 DCOM 文档?

javascript - 将特殊的 HTML 字符插入 XML