簡體   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