简体   繁体   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?

In Python, with subprocess.Popen, is it possible to pass literal quotes as an argument, when the command and its parameters are in list form?在 Python 中,使用 subprocess.Popen,当命令及其参数为列表形式时,是否可以将文字引号作为参数传递?

I'll explain further what I mean.我将进一步解释我的意思。 Some commands can have literal quotes in their arguments eg I'm trying "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --profile-directory="Profile 1" Some might even require them.某些命令可以在其 arguments 中包含文字引号,例如,我正在尝试"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --profile-directory="Profile 1"有些甚至可能需要它们。

Note that one answer points out that technically it is possible to get Chrome from the command line to launch whatever profile, without passing a literal quote C:\Users\User>"C:\Program Files....\chrome.exe" "--profile-directory=Profile 2"请注意,一个答案指出,从技术上讲,可以从命令行获取 Chrome 以启动任何配置文件,而无需传递文字引号C:\Users\User>"C:\Program Files....\chrome.exe" "--profile-directory=Profile 2"

Nevertheless, i'm asking about passing literal quotes so "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --profile-directory="Profile 1"不过,我问的是传递文字引号,所以"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --profile-directory="Profile 1"

For simplicity's sake i'll use calc.exe since it's in the path.为简单起见,我将使用 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()

Now look at the difference in the command line as visible in task manager or via wmic.现在查看在任务管理器中或通过 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>

You can see this from the python interpreter您可以从 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
    
    ....
    >>>

You see it's sticking a backslash in there.您会看到它在其中粘贴了反斜杠。


Some comments regarding some suggestions given.关于给出的一些建议的一些评论。

One suggestion assumes that --profile-directory="Profile 1" is the same as --profile-directory "Profile 1" ie the assumption that you can replace the = with a space and chrome will work the same.一个建议假设--profile-directory="Profile 1"--profile-directory "Profile 1"相同,即假设您可以将 = 替换为空格,并且 chrome 的工作方式相同。 But that isn't the case.但事实并非如此。 So writing subprocess.Popen(["C:\...\chrome.exe", "--profile-directory", "Profile 3"]) will indeed produce "C:\....\chrome.exe" --profile-directory "Profile 1" but that won't work.. it leads chrome to either not open at all, or to open a browser window that offers profiles to click on.所以写subprocess.Popen(["C:\...\chrome.exe", "--profile-directory", "Profile 3"])确实会产生"C:\....\chrome.exe" --profile-directory "Profile 1"但这不起作用.. 它导致 chrome 要么根本不打开,要么打开一个浏览器 window,它提供了可供单击的配置文件。 The equals sign is necessary.等号是必须的。

Another suggestion does另一个建议确实

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

That's not passing a list to Popen, that's passing a list to join, and join is converting it to a string.这不是将列表传递给 Popen,而是将列表传递给 join,而 join 将其转换为字符串。

Another suggestion is另一个建议是

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

That's using a string.那是使用字符串。 But as you see from my question, I managed it using a string.但是正如您从我的问题中看到的那样,我使用字符串对其进行了管理。 I'm asking about using a list.我问的是使用列表。

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

If I run chrome with --profile-directory="Profile 1" I get a particular profile that I use sometimes.如果我使用 --profile-directory="Profile 1" 运行 chrome,我会得到一个我有时使用的特定配置文件。 But if running chrome with "--profile-directory='Profile 1'" Then it doesn't load up that profile.但是,如果使用“--profile-directory='Profile 1'”运行 chrome,那么它不会加载该配置文件。 It loads up a blank profile.它加载一个空白配置文件。 And going to chrome://version shows "'profile 1'" rather than "profile 1" It's like a different profile, like you may as well have said chrome.exe --profile-directory="profile A" .并且去 chrome://version 显示“'profile 1'”而不是“profile 1”这就像一个不同的配置文件,就像你可能已经说过chrome.exe --profile-directory="profile A" And it also creates directories starting with ' like C:\Users\User\AppData\Local\Google\Chrome\User Data\'Profile 1234' that should be removed.它还会创建以'开头的目录,如C:\Users\User\AppData\Local\Google\Chrome\User Data\'Profile 1234'应该删除。

Another suggestion suggested另一个建议建议

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

That is interesting because it does "C:\...chrome.exe" "--profile-directory=Profile 1"这很有趣,因为它执行"C:\...chrome.exe" "--profile-directory=Profile 1"

And it does infact load chrome with the specified profile.它确实使用指定的配置文件加载 chrome。 Though it doesn't try to pass literal quotes!虽然它不会尝试传递文字引号!


My question asks about when passing literal quotes.我的问题询问何时传递文字引号。 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. 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. Though i'm not sure it'd even go to the linux shell on linux.虽然我不确定 go 到 linux shell 上的 ZE206A54E9769086ZEE50CC。 eg on Windows if I stick a cmd escape character in there like ^ so "--pro^file-directory=Profile 1" then the ^ just gets passed literally.例如,在 Windows 上,如果我在其中粘贴一个 cmd 转义字符,如^所以“--pro^file-directory=Profile 1”,那么 ^ 就会按字面意思传递。 So the cmd shell doesn't intervene.所以 cmd shell 不会干预。

Why is it that on Windows, subprocess.Popen calls list2cmdline when passed a list, which(and here's the big 'why'), then adds a backslash to any literal double quote within a string, meaning that when using the 'method' of passing a list to to Popen rather than passing a string to it, there is this problem, that you can't pass a literal double quote, So, why does it add that backslash!为什么在 Windows 上,subprocess.Popen 在传递一个列表时调用 list2cmdline,其中(这里是大的“为什么”),然后在字符串中的任何文字双引号中添加一个反斜杠,这意味着当使用将列表传递给 Popen 而不是将字符串传递给它,存在这个问题,您不能传递文字双引号,那么,为什么要添加反斜杠!

I did here a suggestion that looking at argsv in windows vs linux might show a difference.我在这里做了一个建议,在 windows 与 linux 中查看 argsv 可能会显示出差异。 I'm not sure that they would since both implement C.我不确定他们是否会实现 C。

I don't see why POpen in any situation should behave like Windows needs a backslash inserted more than Linux does.我不明白为什么 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)

in both cases在这两种情况下

    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 required. 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必需的。 (And [here] is put in by the user). (并且 [here] 是由用户输入的)。

That said, I have heard though that looking at what a shell does on Linux is basically misleading, because a major part of the purpose of the subprocess module is to ensure that you can avoid using a shell entirely.话虽如此,但我听说查看 shell 对 Linux 所做的工作基本上是一种误导,因为子进程模块的主要目的是确保您可以完全避免使用 Z2591C98B70119FE62E94898B。

The script example is perhaps not that relevant(it was just something somebody suggested I check), but my issue is that POpen when passed a list is adding in a backslash as shown by WMIC output(also visible in task manager in the command line column).脚本示例可能不那么相关(只是有人建议我检查),但我的问题是 POpen 在传递列表时添加了反斜杠,如 WMIC 输出所示(在命令行列的任务管理器中也可见)。

I spoke to a person that has used python for a long time.我和一个长期使用 python 的人交谈过。 They said subprocess was added somewhere in 2.x They still use os.popen().他们说子进程是在 2.x 的某个地方添加的,他们仍然使用 os.popen()。 That takes a string not a list.这需要一个字符串而不是一个列表。 There have been moves to shift people from os.popen to subprocess.Popen https://docs.python.org/3/library/subprocess.html#replacing-os-popen-os-popen2-os-popen3已经有人将人们从 os.popen 转移到subprocess.Popen https://docs.python.org/3/library/subprocess.html#replacing-os-popen-os-popen2-os-popen3

An issue with subprocess.Popen in Windows, is it has this list feature, that I think behaves funny. Windows 中的 subprocess.Popen 存在问题,它是否具有此列表功能,我认为这很有趣。

The easy workaround to that is to not use the list feature of it.简单的解决方法是不使用它的列表功能。 To not pass it a list.不通过它的列表。 It's a new feature and not necessary.这是一个新功能,没有必要。 You can pass it a string.你可以给它一个字符串。

The question includes an example from the python interpreter and shows how (on windows at least), python adds a backslash to the literal quote.该问题包括来自 python 解释器的示例,并显示了(至少在 windows 上),python 如何在文字引号中添加反斜杠。

The person I spoke to pointed out to me two documents that relate to that.与我交谈的人向我指出了与此相关的两份文件。

A string is a sequence.字符串是一个序列。 A sequence could be a string or list or tuple, though in this document they use the term sequence to just mean list or tuple, and they don't mean string when they say sequence.序列可以是字符串或列表或元组,尽管在本文档中他们使用术语序列仅表示列表或元组,并且当他们说序列时并不表示字符串。

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

"class Popen(args........." “类Popen(参数............”
"args should be a string, or a sequence of program arguments" “args 应该是一个字符串,或一系列程序参数”

It mentions about on unix, shell=True and shell=False它提到关于 unix,shell=True 和 shell=False

And then it says "On Windows: the Popen class uses CreateProcess() to execute the child program, which operates on strings. If args is a sequence, it will be converted to a string using the list2cmdline method. Please note that not all MS Windows applications interpret the command line the same way: The list2cmdline is designed for applications using the same rules as the MS C runtime."然后它说“在 Windows 上:Popen class 使用 CreateProcess() 执行子程序,该程序对字符串进行操作。如果 args 是一个序列,它将使用 list2cmdline 方法将其转换为字符串。请注意,并非所有 MS Windows 应用程序以相同的方式解释命令行:list2cmdline 专为使用与 MS C 运行时相同规则的应用程序而设计。”

Technically a string is a sequence, but that document uses the term sequence in a funny way.从技术上讲,字符串是一个序列,但该文档以一种有趣的方式使用了术语序列。 But what it means is it's that on Windows, if args is not given a string, but is given a list or tuple, then it uses the list2cmdline method.但这意味着在 Windows 上,如果没有给 args 一个字符串,而是给了一个列表或元组,那么它使用 list2cmdline 方法。

Be sure to use print otherwise it uses repr() of the string一定要使用 print 否则它会使用字符串的 repr()


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

so that's the function that it's using behind the scenes, on windows, that is inserting a backslash in there.这就是它在幕后使用的 function,在 windows 上,在那里插入一个反斜杠。

The guy I spoke to pointed me to this document too我与之交谈的那个人也向我指出了这份文件

https://bugs.python.org/issue11827 https://bugs.python.org/issue11827

a technical user comments, "list2cmdline() in subprocess is publicly accessible (doesn't begin with underscores) but it isn't documented."一位技术用户评论说,“子进程中的 list2cmdline() 可以公开访问(不以下划线开头),但没有记录。”

And the point is made there that, let's say they made list2cmdline() private, the fact is that what Popen is doing to the list, in Windows, to get the command line, is undocumented.这里的重点是,假设他们将 list2cmdline() 设为私有,事实是 Popen 对列表所做的事情,在 Windows 中,以获取命令行,是未记录的。

So it seems to be a bad design, I can't see a justification for the insertion of backslash.所以这似乎是一个糟糕的设计,我看不出插入反斜杠的理由。 If a programmer wanted to insert a backslash they could do so.如果程序员想要插入反斜杠,他们可以这样做。 It seeems to me to make more sense then to avoid passing a list to subprocess.POpen.在我看来,避免将列表传递给 subprocess.POpen 更有意义。

Windows cmd doesn't even use backslash as an escape character.!!! Windows cmd 甚至不使用反斜杠作为转义字符。!!! It uses caret.它使用插入符号。

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

C:\Users\User>echo ^\
\

C:\Users\User>

it's linux eg bash, that uses backslash as an escape character它是 linux 例如 bash,它使用反斜杠作为转义字符

$ echo \\
\

$

Some executables in windows might want a quote escaped and with a backslash, but then the technical user can do that just as a technical linux user does. windows 中的某些可执行文件可能需要转义引号并带有反斜杠,但是技术用户可以像技术 linux 用户一样执行此操作。

So given that they haven't even documented the "feature" (or bug), how they would justify it, I don't know, but they could start by documenting it!因此,鉴于他们甚至没有记录“功能”(或错误),他们将如何证明它是合理的,我不知道,但他们可以从记录它开始!

Should do the trick ( might wanna pass shell=True to Popen if it doesn't ):应该做到这一点(如果没有,可能想将shell=True传递给 Popen ):

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

This is possible because --some-flag="some value" is the same as --some-flag "some value"这是可能的,因为--some-flag="some value"--some-flag "some value"相同

ChRomE solution (working, omg): 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