繁体   English   中英

在 Python 中,使用 subprocess.Popen,当 Popen 的命令行参数为列表形式时,是否可以将文字引号传递给要运行的命令?

[英]In Python, with subprocess.Popen, is it possible to pass literal quotes to the command to be run, when Popen's command line parameter is in list form?

在 Python 中,使用 subprocess.Popen,当命令及其参数为列表形式时,是否可以将文字引号作为参数传递?

我将进一步解释我的意思。 某些命令可以在其 arguments 中包含文字引号,例如,我正在尝试"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --profile-directory="Profile 1"有些甚至可能需要它们。

请注意,一个答案指出,从技术上讲,可以从命令行获取 Chrome 以启动任何配置文件,而无需传递文字引号C:\Users\User>"C:\Program Files....\chrome.exe" "--profile-directory=Profile 2"

不过,我问的是传递文字引号,所以"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --profile-directory="Profile 1"

为简单起见,我将使用 calc.exe,因为它在路径中。

import time
import subprocess

proc=subprocess.Popen("calc.exe"+" "+'--profile-directory="Profile 3"')
proc2=subprocess.Popen(["calc.exe",'--profile-directory="Profile 4"'])

time.sleep(3)

proc.wait()
proc2.wait()

现在查看在任务管理器中或通过 wmic 可见的命令行中的差异。

    C:\Users\User>wmic process where caption="calc.exe" get commandline  | findstr calc
    c:\windows\system32\calc.exe --profile-directory="Profile 3"
    c:\windows\system32\calc.exe "--profile-directory=\"Profile 4\""
    
    C:\Users\User>

您可以从 python 解释器中看到这一点

    >>> subprocess.Popen(["c:/windows/system32/calc.exe","abc"+'"'+"def"])
    ...
    >>>
    >>> subprocess.run("C:\Windows\System32\wbem\WMIC.exe process where caption=\"calc.exe\" get commandline")
    ...
    c:/windows/system32/calc.exe abc\"def
    
    ....
    >>>

您会看到它在其中粘贴了反斜杠。


关于给出的一些建议的一些评论。

一个建议假设--profile-directory="Profile 1"--profile-directory "Profile 1"相同,即假设您可以将 = 替换为空格,并且 chrome 的工作方式相同。 但事实并非如此。 所以写subprocess.Popen(["C:\...\chrome.exe", "--profile-directory", "Profile 3"])确实会产生"C:\....\chrome.exe" --profile-directory "Profile 1"但这不起作用.. 它导致 chrome 要么根本不打开,要么打开一个浏览器 window,它提供了可供单击的配置文件。 等号是必须的。

另一个建议确实

subprocess.Popen(
    " ".join(
        [
            "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
            '--profile-directory="Person 1"',
        ]
    )

这不是将列表传递给 Popen,而是将列表传递给 join,而 join 将其转换为字符串。

另一个建议是

subprocess.Popen('C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe --profile-directory="Profile 3"')

那是使用字符串。 但是正如您从我的问题中看到的那样,我使用字符串对其进行了管理。 我问的是使用列表。

另一个建议建议"--profile-directory='Profile 1'"

如果我使用 --profile-directory="Profile 1" 运行 chrome,我会得到一个我有时使用的特定配置文件。 但是,如果使用“--profile-directory='Profile 1'”运行 chrome,那么它不会加载该配置文件。 它加载一个空白配置文件。 并且去 chrome://version 显示“'profile 1'”而不是“profile 1”这就像一个不同的配置文件,就像你可能已经说过chrome.exe --profile-directory="profile A" 它还会创建以'开头的目录,如C:\Users\User\AppData\Local\Google\Chrome\User Data\'Profile 1234'应该删除。

另一个建议建议

subprocess.Popen(
    [
        "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
        "--profile-directory=Profile 1",
    ]

这很有趣,因为它执行"C:\...chrome.exe" "--profile-directory=Profile 1"

它确实使用指定的配置文件加载 chrome。 虽然它不会尝试传递文字引号!


我的问题询问何时传递文字引号。 It's as if maybe it assumes it's a linux shell and inserts a backslash before it, which in a linux would ensure the quote makes it past the shell and to the program being run. 虽然我不确定 go 到 linux shell 上的 ZE206A54E9769086ZEE50CC。 例如,在 Windows 上,如果我在其中粘贴一个 cmd 转义字符,如^所以“--pro^file-directory=Profile 1”,那么 ^ 就会按字面意思传递。 所以 cmd shell 不会干预。

为什么在 Windows 上,subprocess.Popen 在传递一个列表时调用 list2cmdline,其中(这里是大的“为什么”),然后在字符串中的任何文字双引号中添加一个反斜杠,这意味着当使用将列表传递给 Popen 而不是将字符串传递给它,存在这个问题,您不能传递文字双引号,那么,为什么要添加反斜杠!

我在这里做了一个建议,在 windows 与 linux 中查看 argsv 可能会显示出差异。 我不确定他们是否会实现 C。

我不明白为什么 POpen 在任何情况下都应该表现得像 Windows 需要插入比 Linux 更多的反斜杠。


    $ cat ./testargs.py
    #!/usr/bin/env python3
    import sys
    print(sys.argv)
    
    C:\blah>type .\testargsw.py
    import sys
    print(sys.argv)

在这两种情况下

    C:\blah>.\testargsw.py abc\^"def
    ['C:\\Users\\User\\testargsw.py', 'abc"def']
    
    >.\testargsw.py abc\"def
    ['C:\\Users\\User\\testargsw.py', 'abc"def']
    
    C:\blah>
    
    $ ./testargs.py abc\"def
    ['./testargs.py', 'abc"def']

Maybe Windows, specifically the MS C Runtime.. The Code responsible for sending a program's arguments received from the shell, to the main method into argv, is requiring an extra backslash, in a sense because after escaping the double quote, a backslash is then必需的。 (并且 [here] 是由用户输入的)。

话虽如此,但我听说查看 shell 对 Linux 所做的工作基本上是一种误导,因为子进程模块的主要目的是确保您可以完全避免使用 Z2591C98B70119FE62E94898B。

脚本示例可能不那么相关(只是有人建议我检查),但我的问题是 POpen 在传递列表时添加了反斜杠,如 WMIC 输出所示(在命令行列的任务管理器中也可见)。

我和一个长期使用 python 的人交谈过。 他们说子进程是在 2.x 的某个地方添加的,他们仍然使用 os.popen()。 这需要一个字符串而不是一个列表。 已经有人将人们从 os.popen 转移到subprocess.Popen https://docs.python.org/3/library/subprocess.html#replacing-os-popen-os-popen2-os-popen3

Windows 中的 subprocess.Popen 存在问题,它是否具有此列表功能,我认为这很有趣。

简单的解决方法是不使用它的列表功能。 不通过它的列表。 这是一个新功能,没有必要。 你可以给它一个字符串。

该问题包括来自 python 解释器的示例,并显示了(至少在 windows 上),python 如何在文字引号中添加反斜杠。

与我交谈的人向我指出了与此相关的两份文件。

字符串是一个序列。 序列可以是字符串或列表或元组,尽管在本文档中他们使用术语序列仅表示列表或元组,并且当他们说序列时并不表示字符串。

https://peps.python.org/pep-0324/

“类Popen(参数............”
“args 应该是一个字符串,或一系列程序参数”

它提到关于 unix,shell=True 和 shell=False

然后它说“在 Windows 上:Popen class 使用 CreateProcess() 执行子程序,该程序对字符串进行操作。如果 args 是一个序列,它将使用 list2cmdline 方法将其转换为字符串。请注意,并非所有 MS Windows 应用程序以相同的方式解释命令行:list2cmdline 专为使用与 MS C 运行时相同规则的应用程序而设计。”

从技术上讲,字符串是一个序列,但该文档以一种有趣的方式使用了术语序列。 但这意味着在 Windows 上,如果没有给 args 一个字符串,而是给了一个列表或元组,那么它使用 list2cmdline 方法。

一定要使用 print 否则它会使用字符串的 repr()


    >>> print(subprocess.list2cmdline(['a', '"b c"']))
    a "\"b c\""
    >>>

这就是它在幕后使用的 function,在 windows 上,在那里插入一个反斜杠。

我与之交谈的那个人也向我指出了这份文件

https://bugs.python.org/issue11827

一位技术用户评论说,“子进程中的 list2cmdline() 可以公开访问(不以下划线开头),但没有记录。”

这里的重点是,假设他们将 list2cmdline() 设为私有,事实是 Popen 对列表所做的事情,在 Windows 中,以获取命令行,是未记录的。

所以这似乎是一个糟糕的设计,我看不出插入反斜杠的理由。 如果程序员想要插入反斜杠,他们可以这样做。 在我看来,避免将列表传递给 subprocess.POpen 更有意义。

Windows cmd 甚至不使用反斜杠作为转义字符。!!! 它使用插入符号。

C:\Users\User>echo \\
\\

C:\Users\User>echo ^\
\

C:\Users\User>

它是 linux 例如 bash,它使用反斜杠作为转义字符

$ echo \\
\

$

windows 中的某些可执行文件可能需要转义引号并带有反斜杠,但是技术用户可以像技术 linux 用户一样执行此操作。

因此,鉴于他们甚至没有记录“功能”(或错误),他们将如何证明它是合理的,我不知道,但他们可以从记录它开始!

应该做到这一点(如果没有,可能想将shell=True传递给 Popen ):

subprocess.Popen(["C:\Program Files (x86)\Google\Chrome\Application\chrome.exe", "--profile-directory", "Profile 3"]);

这是可能的,因为--some-flag="some value"--some-flag "some value"相同

ChRomE 解决方案(工作,天哪):

import subprocess

subprocess.Popen(
    [
        "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
        "--profile-directory=Profile 1",
    ]
)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM