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



15 Answers

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

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

timeout call stdout

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




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

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




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

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



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



有許多不同的庫允許您使用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 user@example.com:.') # 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 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版本的兼容性,則從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'>]






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



它可以很簡單:

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



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




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




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

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

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



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



在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



Related