subprocess模塊還提供了很多方便的方法來使得執行 shell 命令


現在你可以看到它正常地處理了轉義。

注意

實際上你也可以在shell=False那里直接使用一個單獨的字符串作為參數, 但是它必須是命令程序本身,這種做法和在一個列表中定義一個args沒什么區別。而如果當shell=False時候直接執行字符串命令,則會報錯:

>>> subprocess.Popen('echo "Hello world!"', shell=False)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.5/subprocess.py", line 594, in __init__
errread, errwrite)
File "/usr/lib/python2.5/subprocess.py", line 1147, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
如果我們還是堅持使用一個字符串,Python 會認為這個完整的字符串是一個可執行的程序名,而實際上沒有一個叫做echo "Hello world!"的程序,所以報錯了。正確的做法要用 list 分開傳送參數。
檢查 PATH 中的程序
這里有個方法可以找出程序真正的位置:
import os
def whereis(program):
for path in os.environ.get('PATH', '').split(':'):
if os.path.exists(os.path.join(path, program)) and \
not os.path.isdir(os.path.join(path, program)):
return os.path.join(path, program)
return None
讓我們用它來找出echo程序在哪里:
>>> location = whereis('echo')
>>> if location is not None:
... print location
/bin/echo
這個方法同樣可以檢查用戶的PATH里面是否有 Python 需要的程序。

當然你也可以使用命令行中的程序whereis來找出程序的路徑。

$ whereis echo
echo: /bin/echo /usr/share/man/man1/echo.1.gz
注意

無論我們使用shell為True或者False, 我們都沒有指定執行程序的全路徑。 如果這個程序在上下文環境的PATH變量中,我們才可以執行。 當然如果你願意,指定全路徑也沒問題。

你也可以堅持指定executable為想要執行的程序, 然后args就不設定程序。雖然沒看到明確的文檔,不過我電腦上面可以這么執行:

>>> subprocess.Popen(['1', '2', '3'], shell=False, executable='echo')
2 3
<subprocess.Popen object at 0xb776f56c>
不直接使用 shell 會導致不能直觀地使用重定向、管道、here 文檔、shell 參數或其他那些可以在命令行使用的技巧。接下來我們會看看怎么使用這些功能。
從標准輸出和錯誤重定向
當你使用Popen執行程序時候,輸出內容通常被發送到 stdout, 這也是為什么你能看到這些內容。

當你想嘗試從某個程序讀取標准輸出信息時候,則需要在調用Popen之前設定stdout參數。要設定的值是subprocess.PIPE:

subprocess.PIPE

可以為Popen指定標准輸入、標准輸出和標准錯誤輸出的參數, 需要注意的是標准輸出流需要打開可寫。

這里有個范例:

>>> process = subprocess.Popen(['echo', 'Hello World!'], shell=False, stdout=subprocess.PIPE)
To read the output from the pipe you use thecommunicate()method:

為了從管道獲取輸出,你可以使用communicate()方法:

>>> print process.communicate()
('Hello World!\n', None)
communicate()的返回值是一個 tuple,第一個值是標准輸出的數據, 第二個輸出是標准錯誤輸出的內容。
這里有段腳本能讓我們測試標准輸出和標准錯誤輸出的表現行為, 將它存為test1.py:
import sys
sys.stdout.write('Message to stdout\n')
sys.stderr.write('Message to stderr\n')
執行它:
>>> process = subprocess.Popen(['python', 'test1.py'], shell=False, stdout=subprocess.PIPE)
Message to stderr
>>> print process.communicate()
('Message to stdout\n', None)
注意標准錯誤輸出在被生成后就打印了,而標准輸出則被管道傳輸了。 這是因為我們只設定了標准輸出的管道,讓我們同時也設定標准錯誤輸出。
>>> process = subprocess.Popen(['python', 'test1.py'], shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> print process.communicate()
('Message to stdout\n', 'Message to stderr\n')
這次標准輸出和標准錯誤輸出都被 Python 獲取到了。

現在所有的消息能被打印出來了,如果我們再次調用communicate(), 則會得到一個錯誤信息:

>>> print process.communicate()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.5/subprocess.py", line 668, in communicate
return self._communicate(input)
File "/usr/lib/python2.5/subprocess.py", line 1207, in _communicate
rlist, wlist, xlist = select.select(read_set, write_set, [])
ValueError: I/O operation on closed file
communicate()方法讀取標准輸出和標准錯誤輸出時候,遇到結束符(EOF) 就會結束。
重定向 stderr 到 stdout
如果你想將錯誤信息重定向到標准輸出,只需要給stderr參數指定一個特殊值:stderr=subprocess.STDOUT即可。
寫入標准輸入
寫數據入一個進程和之前所述比較類似。為了要寫入數據,需要先打開一個管道到標准輸入。 通過設定Popen參數stdin=subproces.PIPE可以實現。
為了測試,讓我們另外寫一個僅輸出Received:和輸入數據的程序。 它在退出之前會輸出消息。調用這個test2.py:
import sys
input = sys.stdin.read()
sys.stdout.write('Received: %s'%input)
為了發送消息到標准輸入,把你想發送的信息作為communicate()的參數input。讓我們跑起來:
>>> process = subprocess.Popen(['python', 'test2.py'], shell=False, stdin=subprocess.PIPE)
>>> print process.communicate('How are you?')
Received: How are you?(None, None)
注意test2.py發送的信息被打印到標准輸出,隨后的是(None, None), 這是因為標准輸出和標准錯誤輸出沒有設定輸出管道。

你可以和之前那樣指定stdout=subprocess.PIPE和stderr=subprocess.PIPE來設定輸出管道。

類文件屬性
Popen擁有stdout和stderr屬性,從而可以當作文件一樣寫出數據,同時stdin屬性可以像文件一樣讀取數據。 你可以使用他們來替換communicate()。下面我們將看如何用它們。
讀寫同一個進程
這里有個例子,將它保存為test3.py:
import sys
while True:
input = sys.stdin.readline()
sys.stdout.write('Received: %s'%input)
sys.stdout.flush()
這個程序也是簡單的響應接受到的數據,讓我們把它跑起來:
>>> import time
>>> process = subprocess.Popen(['python', 'test3.py'], shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
>>> for i in range(5):
... process.stdin.write('%d\n' % i)
... output = process.stdout.readline()
... print output
... time.sleep(1)
...
Received: 0

Received: 1

Received: 2

Received: 3

Received: 4

>>>
每隔一秒鍾會輸出一行。

現在你應該掌握了所有需要通過 Python 來跟 Shell 交互需要的知識。

獲取返回值,poll()和wait()
當一個程序退出時候,他會返回一個正整數來表明它的退出狀態。 0 代表「成功地結束」,非零則表示「非正常結束」。 大部分系統要求返回值在 0-127 之間,其他都是未定義的結果。 一些系統會有事先定義好的錯誤對應關系,但一般不被拿出來用。 Unix 程序通常使用 2 作為命令語法錯誤,1 作為其他錯誤。
你可以通過Popen的.returncode屬性獲取程序返回值。這兒有個例子:
>>> process = subprocess.Popen(['echo', 'Hello world!'], shell=False)
>>> process.poll()
>>> print process.returncode
None
>>> process.poll()
0
>>> print process.returncode
0
這個returncode並不是一開始就設定好的,最初是默認值None, 它會一直是None知道你調用subprocess的方法比如poll()和wait()。 這些方法會設定returncode。因此,如果你想知道返回值,那就調用poll(2881064151)和wait()。

poll()和wait()方法區別很小:

Popen.poll(): 檢查子進程是否結束。並設置和返回.returncode屬性。Popen.wait(): 等待子進程結束。並設置和返回.returncode屬性。

便捷的方法
subprocess模塊還提供了很多方便的方法來使得執行 shell 命令更方便。 我沒有全部試試。(譯者:意思是讓讀者自己挖掘?)
理解sys.argv
如果你想寫一個 Python 腳本來接受命令行參數, 那么命令行的參數會被傳送並成參數sys.argv。 這里有個小范例,將它保存成command.py。
#!/usr/bin/env python
if __name__ == '__main__':
import sys
print "Executable: %s"%sys.argv[0]
for arg in sys.argv[1:]:
print "Arg: %s"%arg
if __name__ == '__main__'這行確保代碼在被執行是才運行, 而不是被引入時候運行。給這個文件執行權限:
1
$ chmod 755 command.py
這里是一些運行時的范例:
$ python command.py
Executable: command.py
$ python command.py arg1
Executable: command.py
Arg: arg1
$ python command.py arg1 arg2
Executable: command.py
Arg: arg1
Arg: arg2
注意無論 Python 腳本怎么執行,sys.argv[0]始終是腳本的名稱。sys.argv[1]和之后的參數是命令行接受的參數。 你可以通過使用參數-m來強制 Python 腳本作為模塊導入使用。
$ python -m command
Executable: /home/james/Desktop/command.py
$ python -m command arg1
Executable: /home/james/Desktop/command.py
Arg: arg1
$ python -m command arg1 arg2
Executable: /home/james/Desktop/command.py
Arg: arg1
Arg: arg2
如你所見,Python 將-m作為命令的一部分,因此 `sys.srgv[0] 包含了腳本的全路徑。 現在我們來直接執行它:
$ ./command.py
Executable: ./command.py
$ ./command.py arg1
Executable: ./command.py
Arg: arg1
$ ./command.py arg1 arg2
Executable: ./command.py
Arg: arg1
Arg: arg2
看吧,sys.argv[0]包含 Python 腳本的名稱,sys.argv[1]以及他的兄弟們還是老樣子,包含各類參數。
展開 Shell
有時候,我們會在 shell 中使用通配符來設定一組參數,比如, 我們在 Bash 中運行:
$ ./command.py *.txt
你可能覺得輸出應該是:
Executable: ./command.py
Arg: *.txt
這不是你想要的結果。輸出結果應該依賴當前文件夾中.txt文件的數目。執行效果如下:
Executable: ./command.py
Arg: errors.txt
Arg: new.txt
Arg: output.txt
Bash 會將\*.txt自動展開成所有符合.txt的參數。所以接受到的參數會超過你預期。

你可以通過將參數用引號抱起來來關閉 Shell 解釋特性, 但是只要你用過,就會意識到在大多數情況下面這是非常有用的功能。

$ ./command.py "*.txt"
Executable: ./command.py
Arg: *.txt


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM