subprocess用法 - subprocess python




在Python中調用外部命令 (20)

如何在Python腳本中調用外部命令(就像我在Unix shell或Windows命令提示符下鍵入它一樣)?


在Python中調用外部命令

簡單,使用subprocess.run ,它返回一個CompletedProcess對象:

>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)

為什麼?

從Python 3.5開始,文檔推薦使用subprocess.run

調用子進程的推薦方法是對它可以處理的所有用例使用run()函數。 對於更高級的用例,可以直接使用底層的Popen接口。

這是一個最簡單的使用示例 - 它完全按照要求:

>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)

run等待命令成功完成,然後返回CompletedProcess對象。 它可能會引發TimeoutExpired (如果你給它一個timeout=參數)或CalledProcessError (如果它失敗並且你傳遞check=True )。

正如您可能從上面的示例中推斷的那樣,默認情況下,stdout和stderr都會通過管道傳輸到您自己的stdout和stderr。

我們可以檢查返回的對象並查看給出的命令和返回碼:

>>> completed_process.args
'python --version'
>>> completed_process.returncode
0

捕獲輸出

如果要捕獲輸出,可以將subprocess.PIPE傳遞給相應的stderrstdout

>>> cp = subprocess.run('python --version', 
                        stderr=subprocess.PIPE, 
                        stdout=subprocess.PIPE)
>>> cp.stderr
b'Python 3.6.1 :: Anaconda 4.4.0 (64-bit)\r\n'
>>> cp.stdout
b''

(我覺得有趣且有點違反直覺,版本信息被放到stderr而不是stdout。)

傳遞命令列表

人們可以輕鬆地從手動提供命令字符串(如問題建議)到提供以編程方式構建的字符串。 不要以編程方式構建字符串。 這是一個潛在的安全問題。 假設您不信任輸入,那就更好了。

>>> import textwrap
>>> args = ['python', textwrap.__file__]
>>> cp = subprocess.run(args, stdout=subprocess.PIPE)
>>> cp.stdout
b'Hello there.\r\n  This is indented.\r\n'

注意,只有args應該在位置上傳遞。

完整簽名

這是源代碼中的實際簽名,如help(run)

def run(*popenargs, input=None, timeout=None, check=False, **kwargs):

popenargskwargs被賦予popenargs構造函數。 input可以是一串字節(或unicode,如果指定encoding或universal_newlines=True ),它將被傳送到子進程的stdin。

該文檔描述了timeout=check=True比我更好:

timeout參數傳遞給Popen.communicate()。 如果超時到期,子進程將被終止並等待。 子進程終止後,將重新引發TimeoutExpired異常。

如果check為true,並且進程以非零退出代碼退出,則將引發CalledProcessError異常。 該異常的屬性包含參數,退出代碼以及stdout和stderr(如果它們被捕獲)。

這個例子對於check=True比我想出的更好:

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
  ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

擴展簽名

這是一個擴展的簽名,如文檔中所示:

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, 
shell=False, cwd=None, timeout=None, check=False, encoding=None, 
errors=None)

請注意,這表示只應按位置傳遞args列表。 因此將剩餘的參數作為關鍵字參數傳遞。

POPEN

什麼時候使用Popen ? 我很難根據論據單獨找到用例。 但是,直接使用Popen可以訪問其方法,包括poll ,'send_signal','terminate'和'wait'。

這是源中給出的Popen簽名。 我認為這是對信息的最精確封裝(與help(Popen)相反):

def __init__(self, args, bufsize=-1, executable=None,
             stdin=None, stdout=None, stderr=None,
             preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
             shell=False, cwd=None, env=None, universal_newlines=False,
             startupinfo=None, creationflags=0,
             restore_signals=True, start_new_session=False,
             pass_fds=(), *, encoding=None, errors=None):

但更多信息是Popen文檔

subprocess.Popen(args, bufsize=-1, executable=None, stdin=None,
                 stdout=None, stderr=None, preexec_fn=None, close_fds=True,
                 shell=False, cwd=None, env=None, universal_newlines=False,
                 startupinfo=None, creationflags=0, restore_signals=True,
                 start_new_session=False, pass_fds=(), *, encoding=None, errors=None)

在新進程中執行子程序。 在POSIX上,該類使用os.execvp() - 類似行為來執行子程序。 在Windows上,該類使用Windows CreateProcess()函數。 Popen的論點如下。

了解Popen的剩余文檔將留給讀者練習。


使用標準庫

使用子進程模塊 (Python 3):

import subprocess
subprocess.run(['ls', '-l'])

這是推薦的標準方式。 但是,更複雜的任務(管道,輸出,輸入等)構造和寫入可能很繁瑣。

關於Python版本的注意事項:如果您仍在使用Python 2,則subprocess.call以類似的方式工作。

ProTip: docs.python.org/2/library/shlex.html#shlex.split可以幫助您解析runcall和其他subprocess docs.python.org/2/library/shlex.html#shlex.split函數的命令,以防您不希望(或者您不能!)以列表的形式提供它們:

import shlex
import subprocess
subprocess.run(shlex.split('ls -l'))

具有外部依賴性

如果您不介意外部依賴項,請使用plumbum

from plumbum.cmd import ifconfig
print(ifconfig['wlan0']())

它是最好的subprocess包裝器。 它是跨平台的,即它適用於Windows和類Unix系統。 通過pip install plumbum

另一個受歡迎的圖書館是:

from sh import ifconfig
print(ifconfig('wlan0'))

但是, sh刪除了Windows支持,所以它不像過去那樣棒。 通過pip install sh



os.system不允許您存儲結果,因此如果您想將結果存儲在某些列表或os.system中。


以下是調用外部程序的方法及其優缺點的摘要:

  1. os.system("some_command with args")將命令和參數傳遞給系統的shell。 這很好,因為您實際上可以以這種方式一次運行多個命令並設置管道和輸入/輸出重定向。 例如:

    os.system("some_command < input_file | another_command > output_file")  
    

    但是,雖然這很方便,但您必須手動處理shell字符的轉義,例如空格等。另一方面,這也允許您運行只是shell命令而不是外部程序的命令。 請參閱文檔

  2. stream = os.popen("some_command with args")將執行與os.system相同的操作,除了它為您提供了一個類似文件的對象,您可以使用它來訪問該進程的標準輸入/輸出。 還有3種其他的popen變體,它們對i / o的處理方式略有不同。 如果您將所有內容都作為字符串傳遞,那麼您的命令將傳遞給shell; 如果你將它們作為列表傳遞,那麼你不必擔心逃避任何事情。 請參閱文檔

  3. subprocess模塊的Popen類。 這是為了取代os.popen但由於功能如此全面而具有稍微複雜的缺點。 例如,你會說:

    print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()
    

    代替:

    print os.popen("echo Hello World").read()
    

    但是在一個統一的類而不是4個不同的popen函數中擁有所有選項是很好的。 請參閱文檔

  4. subprocess模塊的call函數。 這基本上就像Popen類一樣,並且接受所有相同的參數,但它只是等待命令完成並為您提供返回代碼。 例如:

    return_code = subprocess.call("echo Hello World", shell=True)  
    

    請參閱文檔

  5. 如果您使用的是Python 3.5或更高版本,則可以使用新的subprocess.run函數,該函數與上述類似,但更靈活,並在命令完成執行時返回CompletedProcess對象。

  6. os模塊還具有C程序中的所有fork / exec / spawn函數,但我不建議直接使用它們。

subprocess模塊可能應該是你使用的。

最後請注意,對於所有將shell執行的最終命令作為字符串傳遞的方法,您負責轉義它。 如果您傳遞的字符串的任何部分無法完全受信任,則會產生嚴重的安全隱患 。 例如,如果用戶正在輸入字符串的某些/任何部分。 如果您不確定,請僅將這些方法與常量一起使用。 為了給您一些暗示,請考慮以下代碼:

print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()

並想像用戶輸入“我的媽媽不愛我&& rm -rf /”。


你可以使用Popen,然後你可以檢查程序的狀態:

from subprocess import Popen

proc = Popen(['ls', '-l'])
if proc.poll() is None:
    proc.kill()

檢查subprocess.Popen


如果您不想測試返回值,則subprocess.check_call很方便。 它會對任何錯誤拋出異常。


如果您需要調用命令的輸出,則可以使用subprocess.check_output(Python 2.7+)。

>>> subprocess.check_output(["ls", "-l", "/dev/null"])
'crw-rw-rw- 1 root root 1, 3 Oct 18  2007 /dev/null\n'

另請注意shell參數。

如果shell為True ,則將通過shell執行指定的命令。 如果您主要使用Python來提供它在大多數係統shell上提供的增強控制流,並且仍然希望方便地訪問其他shell功能,例如shell管道,文件名通配符,環境變量擴展以及擴展到用戶家中,這將非常有用。目錄。 但請注意,Python本身提供了許多類似shell的功能的實現(特別是globfnmatchos.walk()os.path.expandvars()os.path.expanduser()shutil )。


我傾向於將 shlexshlex一起使用(以處理引用字符串的轉義):

>>> import subprocess, shlex
>>> command = 'ls -l "/your/path/with spaces/"'
>>> call_params = shlex.split(command)
>>> print call_params
["ls", "-l", "/your/path/with spaces/"]
>>> subprocess.call(call_params)

我建議使用子進程模塊而不是os.system,因為它為你做了shell轉義,因此更加安全: http://docs.python.org/library/subprocess.html ://docs.python.org/library/subprocess.html

subprocess.call(['ping', 'localhost'])

我通常使用:

import subprocess

p = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
    print line,
retval = p.wait()

您可以使用管道中的stdout數據自由地執行所需操作。 實際上,您可以簡單地省略這些參數( stdout=stderr= ),它的行為類似於os.system()


有許多不同的庫允許您使用Python調用外部命令。 對於每個庫,我已經給出了描述並顯示了調用外部命令的示例。 我用作示例的命令是ls -l (列出所有文件)。 如果您想了解有關我列出的任何庫的更多信息,並鏈接每個庫的文檔。

資料來源:

這些都是庫:

希望這可以幫助您決定使用哪個庫:)

子進程允許您調用外部命令並將它們連接到它們的輸入/輸出/錯誤管道(stdin,stdout和stderr)。 子進程是運行命令的默認選擇,但有時其他模塊更好。

subprocess.run(["ls", "-l"]) # Run command
subprocess.run(["ls", "-l"], stdout=subprocess.PIPE) # This will run the command and return any output
subprocess.run(shlex.split("ls -l")) # You can also use the shlex library to split the command

os用於“依賴於操作系統的功能”。 它也可以用於使用os.systemos.popen調用外部命令(注意:還有一個os.popen )。 os將始終運行shell,對於不需要或不知道如何使用subprocess.run人來說,它是一個簡單的替代方案。

os.system("ls -l") # run command
os.popen("ls -l").read() # This will run the command and return any output

SH

sh是一個子進程接口,它允許您像調用函數一樣調用程序。 如果要多次運行命令,這非常有用。

sh.ls("-l") # Run command normally
ls_cmd = sh.Command("ls") # Save command as a variable
ls_cmd() # Run command as if it were a function

plumbum是一個用於“類似腳本”的Python程序的庫。 您可以像sh一樣調用函數之類的程序。 如果要運行沒有shell的管道,Plumbum很有用。

ls_cmd = plumbum.local("ls -l") # get command
ls_cmd() # run command

Pexpect的

pexpect允許您生成子應用程序,控制它們並在其輸出中查找模式。 對於期望在Unix上使用tty的命令,這是替代子進程的更好的替代方法。

pexpect.run("ls -l") # Run command as normal
child = pexpect.spawn('scp foo [email protected]:.') # Spawns child application
child.expect('Password:') # When this is the output
child.sendline('mypassword')

fabric是一個Python 2.5和2.7庫。 它允許您執行本地和遠程shell命令。 Fabric是在安全shell(SSH)中運行命令的簡單替代方法

fabric.operations.local('ls -l') # Run command as normal
fabric.operations.local('ls -l', capture = True) # Run command and receive output

使者

特使被稱為“人類的子過程”。 它用作subprocess模塊周圍的便利包裝器。

r = envoy.run("ls -l") # Run command
r.std_out # get output

命令

commands包含os.popen包裝函數,但它已從Python 3中刪除,因為subprocess os.popen是一個更好的選擇。

該編輯基於JF Sebastian的評論。


查看標準庫中的子進程模塊

from subprocess import call
call(["ls", "-l"])

子進程系統的優勢在於它更靈活(您可以獲得stdout,stderr,“真實”狀態代碼,更好的錯誤處理等)。

官方文檔推薦使用替代os.system()的進程模塊:

子流程模塊提供了更強大的工具來生成新流程並檢索其結果; 使用該模塊比使用此函數[ os.system() ]更os.system()

子流程文檔中的“ 用子流程模塊替換舊函數 ”部分可能有一些有用的配方。

子進程模塊的官方文檔:


檢查“pexpect”Python庫。

它允許交互式控制外部程序/命令,甚至ssh,ftp,telnet等。您只需鍵入以下內容:

child = pexpect.spawn('ftp 192.168.0.24')

child.expect('(?i)name .*: ')

child.sendline('anonymous')

child.expect('(?i)password')

這就是我運行命令的方式。 此代碼包含您需要的所有內容

from subprocess import Popen, PIPE
cmd = "ls -l ~/"
p = Popen(cmd , shell=True, stdout=PIPE, stderr=PIPE)
out, err = p.communicate()
print "Return code: ", p.returncode
print out.rstrip(), err.rstrip()

這裡有另一個不同之處,前面沒有提到過。

subprocess.Popen將<command>作為subprocess.Popen執行。 在我的例子中,我需要執行需要與另一個程序進行通信的文件<a> <b>。

我嘗試了subprocess,執行成功了。 但是<b>無法與<a>通信。 當我從終端運行時,一切正常。

還有一個:(注意:kwrite的行為與其他應用程序不同。如果您使用Firefox嘗試以下操作,結果將不同。)

如果你嘗試os.system("kwrite") ,程序流程將凍結,直到用戶關閉kwrite。 為了克服這個問題,我嘗試了os.system(konsole -e kwrite) 。 這個時間程序繼續流動,但kwrite成為控制台的子進程。

任何人都運行kwrite不是一個子進程(即在系統監視器中它必須出現在樹的最左邊)。


在Linux下,如果你想調用一個獨立執行的外部命令(將在python腳本終止後繼續運行),你可以使用一個簡單的隊列作為任務假脫機程序at命令

任務假脫機程序的示例:

#!/usr/bin/python
import os
netid= "nova net-list | awk '/ External / { print $2 }'"
temp=os.popen(netid).read()  /* here temp also contains new line (\n) */
networkId=temp.rstrip()
print(networkId)

有關任務假脫機程序(ts)的說明:

  1. 您可以使用以下命令設置要運行的並發進程數(“slots”):

    ts -S <number-of-slots>

  2. 安裝ts不需要管理員權限。您可以從源代碼下載並編譯它make,只需將其添加到您的路徑中即可完成。


在Windows中你可以導入subprocess模塊,並通過調用運行外部命令subprocess.Popen()subprocess.Popen().communicate()subprocess.Popen().wait()如下:

+--------------------------------------+------------+------+
| ID                                   | Label      | CIDR |
+--------------------------------------+------------+------+
| 431c9014-5b5d-4b51-a357-66020ffbb123 | test1      | None |
| 27a74fcd-37c0-4789-9414-9531b7e3f126 | External   | None |
| 5a2712e9-70dc-4b0e-9281-17e02f4684c9 | management | None |
| 7aa697f5-0e60-4c15-b4cc-9cb659698512 | Internal   | None |
+--------------------------------------+------------+------+

輸出:

27a74fcd-37c0-4789-9414-9531b7e3f126

無恥的插件,我為此寫了一個庫:P https://github.com/houqp/shell.py

它現在基本上是popen和shlex的包裝器。它還支持管道命令,因此您可以在Python中更輕鬆地鏈接命令。所以你可以這樣做:

ex('echo hello shell.py') | "awk '{print $2}'"

import os
cmd = 'ls -al'
os.system(cmd)

如果要返回命令的結果,可以使用os.popen 。 但是,自版本2.6以來,這已被棄用,而不是子進程模塊 ,其他答案已經很好地解決了。





external