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




subprocess用法 timeout (25)

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


Answers

我非常喜歡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

沒有結果的輸出:

import os
os.system("your command here")

輸出結果:

import commands
commands.getoutput("your command here")
or
commands.getstatusoutput("your command here")

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


在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

還有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

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 /”。


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

here獲取更多信息。


檢查“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()

使用標準庫

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


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


它可以很簡單:

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

更新:

如果您的代碼不需要保持與早期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'>]

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

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


使用:

import os

cmd = 'ls -al'

os.system(cmd)

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

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


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

假設您想從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中啟動後台進程尚未解決任何問題。


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

from subprocess import Popen

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

檢查subprocess.Popen


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

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

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

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

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

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


我總是使用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)

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

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

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

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

子進程模塊的官方文檔:


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

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

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

這是一個基於這個答案的流程圖。 另請參閱使用script模擬終端 。







python shell command subprocess external