shell subprocess教學 - 在Python中調用外部命令




subprocess用法 timeout (25)

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


Answers

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


還有Plumbum

>>> from plumbum import local
>>> ls = local["ls"]
>>> ls
LocalCommand(<LocalPath /bin/ls>)
>>> ls()
u'build.py\ndist\ndocs\nLICENSE\nplumbum\nREADME.rst\nsetup.py\ntests\ntodo.txt\n'
>>> notepad = local["c:\\windows\\notepad.exe"]
>>> notepad()                                   # Notepad window pops up
u''                                             # Notepad window is closed by user, command returns

有許多不同的庫允許您使用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()

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

子進程模塊的官方文檔:


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

from subprocess import Popen

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

檢查subprocess.Popen


有關將子進程與調用進程分離的一些提示(在後台啟動子進程)。

假設您想從CGI腳本啟動一個長任務,即子進程應該比CGI腳本執行過程更長壽。

子流程模塊docs的經典示例是:

import subprocess
import sys

# some code here

pid = subprocess.Popen([sys.executable, "longtask.py"]) # call subprocess

# some more code here

這裡的想法是你不想在“調用子進程”行中等待,直到longtask.py完成。 但目前尚不清楚在這個例子中“更多代碼”這一行之後會發生什麼。

我的目標平台是freebsd,但是開發是在windows上進行的,所以我首先在windows上遇到了問題。

在Windows(win xp)上,父進程將不會完成,直到longtask.py完成其工作。 這不是你想要的CGI腳本。 問題不是Python特有的,在PHP社區中問題是一樣的。

解決方案是將DETACHED_PROCESS 流程創建標誌傳遞給win API中的底層CreateProcess函數。 如果你碰巧安裝了pywin32,你可以從win32process模塊導入標誌,否則你應該自己定義:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

/ * UPD 2015.10.27 @eryksun在下面的評論中指出,語義正確的標誌是CREATE_NEW_CONSOLE(0x00000010)* /

在freebsd上我們還有另一個問題:父進程完成後,它也會完成子進程。 這也不是你想要的CGI腳本。 一些實驗表明問題似乎在於共享sys.stdout。 工作解決方案如下:

pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)

我沒有檢查其他平台上的代碼,也不知道freebsd上的行為原因。 如果有人知道,請分享您的想法。 谷歌搜索在Python中啟動後台進程尚未解決任何問題。


在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,只需將其添加到您的路徑中即可完成。


更新:

如果您的代碼不需要保持與早期Python版本的兼容性,則從Python 3.5開始 ,推薦使用subprocess.run。 它更加一致,並提供與Envoy類似的易用性。 (管道不是那麼簡單。看看這個問題是怎麼回事 。)

以下是文檔中的一些示例。

運行一個過程:

>>> subprocess.run(["ls", "-l"])  # doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)

提高失敗的運行:

>>> 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(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')

原始答案:

我建議嘗試https://github.com/kennethreitz/envoy 。 它是子進程的包裝器,後者又旨在替換舊的模塊和功能。 特使是人類的子過程。

自述文件中的示例用法:

>>> r = envoy.run('git config', data='data to pipe in', timeout=2)

>>> r.status_code
129
>>> r.std_out
'usage: git config [options]'
>>> r.std_err
''

管道周圍的東西:

>>> r = envoy.run('uptime | pbcopy')

>>> r.command
'pbcopy'
>>> r.status_code
0

>>> r.history
[<Response 'uptime'>]

我傾向於將 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)

使用標準庫

使用子進程模塊 (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


我非常喜歡shell_command因為它簡單。它建立在子進程模塊之上。

以下是文檔中的示例:

>>> from shell_command import shell_call
>>> shell_call("ls *.py")
setup.py  shell_command.py  test_shell_command.py
0
>>> shell_call("ls -l *.py")
-rw-r--r-- 1 ncoghlan ncoghlan  391 2011-12-11 12:07 setup.py
-rw-r--r-- 1 ncoghlan ncoghlan 7855 2011-12-11 16:16 shell_command.py
-rwxr-xr-x 1 ncoghlan ncoghlan 8463 2011-12-11 16:17 test_shell_command.py
0

檢查“pexpect”Python庫。

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

child = pexpect.spawn('ftp 192.168.0.24')

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

child.sendline('anonymous')

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

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


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

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

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

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

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

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


我通常使用:

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()


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

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()

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

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


在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的剩余文檔將留給讀者練習。


使用:

import os

cmd = 'ls -al'

os.system(cmd)

os - 此模塊提供了一種使用操作系統相關功能的便攜方式。

對於更多的os函數, here是文檔。


如果您需要調用命令的輸出,則可以使用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 )。


它可以很簡單:

import os
cmd = "your command"
os.system(cmd)


os.system是好的,但有點過時了。 它也不是很安全。 相反,嘗試subprocessos.system不直接調用sh,因此比os.system更安全。

here獲取更多信息。


我總是使用fabric這樣的事情:

from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )

但這似乎是一個很好的工具: sh (Python子進程接口)

看一個例子:

from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)

語法:Ternary運算符將給出:

[on_true] if [expression] else [on_false]

例如

x, y = 25, 50
big = x if x < y else y
print(big)




python shell command subprocess external