Date: 2019-05-28python
Author: Sunshell
運行一個進程bash
運行python的時候,咱們都是在建立並運行一個進程。像Linux進程那樣,一個進程能夠fork一個子進程,並讓這個子進程exec另一個程序。在Python中,咱們經過標準庫中的subprocess包來fork一個子進程,並運行一個外部的程序。函數
subprocess包中定義有數個建立子進程的函數,這些函數分別以不一樣的方式建立子進程,因此咱們能夠根據須要來從中選取一個使用。code
1. subprocess.call()對象
函數格式以下:進程
call(*popenargs, timeout=None, **kwargs): """Run command with arguments. Wait for command to complete or timeout, then return the returncode attribute. The arguments are the same as for the Popen constructor. Example: retcode = call(["ls", "-l"])
父進程等待子進程完成
返回退出信息(returncode,至關於Linux exit code)utf-8
>>> import subprocess >>> retcode = subprocess.call(["ls", "-l"]) #和shell中命令ls -a顯示結果同樣 >>> print retcode 0
或者是cmd
>>> a = subprocess.call(['df','-hT'],shell=False) Filesystem Type Size Used Avail Use% Mounted on /dev/sda2 ext4 94G 64G 26G 72% / tmpfs tmpfs 2.8G 0 2.8G 0% /dev/shm /dev/sda1 ext4 976M 56M 853M 7% /boot
subprocess.check_call():用法與subprocess.call()相似,區別是,當返回值不爲0時,直接拋出異常it
>>> a = subprocess.check_call('df -hT',shell=True) Filesystem Type Size Used Avail Use% Mounted on /dev/sda2 ext4 94G 64G 26G 72% / tmpfs tmpfs 2.8G 0 2.8G 0% /dev/shm /dev/sda1 ext4 976M 56M 853M 7% /boot >>> print a 0 >>> a = subprocess.check_call('dfdsf',shell=True) /bin/sh: dfdsf: command not found Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib64/python2.6/subprocess.py", line 502, in check_call raise CalledProcessError(retcode, cmd) subprocess.CalledProcessError: Command 'dfdsf' returned non-zero exit status 127
2. subprocess.Popen()
在一些複雜場景中,咱們須要將一個進程的執行輸出做爲另外一個進程的輸入。在另外一些場景中,咱們須要先進入到某個輸入環境,而後再執行一系列的指令等。這個時候咱們就須要使用到suprocess的Popen()方法。
函數形式以下:
class Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)
Popen對象建立後,主程序不會自動等待子進程完成。咱們必須調用對象的wait()方法,父進程纔會等待 (也就是阻塞block)
import subprocess if __name__ == "__main__": child = subprocess.Popen('ping -c www.baidu.com', shell=True) child.wait() print('parent process')
父進程在開啓子進程以後並等待child的完成後,再運行print。
此外,你還能夠在父進程中對子進程進行其它操做,好比咱們上面例子中的child對象:代碼以下:
child.poll() # 檢查子進程狀態
child.kill() # 終止子進程
child.send_signal() # 向子進程發送信號
child.terminate() # 終止子進程
子進程的標準輸入、標準輸出和標準錯誤, 以下屬性分別表示:
child.stdin
child.stdout
child.stderr
示例,將一個子進程的輸出,做爲另外一個子進程的輸入:
import subprocess child1 = subprocess.Popen(["cat","/etc/passwd"], stdout=subprocess.PIPE) child2 = subprocess.Popen(["grep","0:0"],stdin=child1.stdout, stdout=subprocess.PIPE) out = child2.communicate()
案例分析:
在工做中常常會遇到這樣的需求:
須要採用python來運行一個shell腳本,如何優雅的操做呢?
解決方案:
用python的subprocess去執行傳遞過來的腳本,一般狀況下subprocess都能運行的很好,完成腳本的執行並返回。
能夠採用以下代碼實現:
# -*- coding: utf-8 -*- __author__ = 'sun' __date__ = '2019/5/28 18:26' import subprocess from threading import Timer import os import time import signal class TestSubProcess(object): def __init__(self): self.stdout = [] self.stderr = [] self.timeout = 6 self.is_timeout = False def timeout_callback(self, p): print('exe time out call back') try: p.kill() # os.killpg(p.pid, signal.SIGKILL) except Exception as error: print(error) def run(self): stdout = open('/tmp/subprocess_stdout', 'wb') stderr = open('/tmp/subprocess_stderr', 'wb') cmd = ['bash', '/home/xxx/while_test.sh'] ps = subprocess.Popen(cmd, stdout=stdout.fileno(), stderr=stderr.fileno()) my_timer = Timer(self.timeout, self.timeout_callback, [ps]) my_timer.start() print(ps.pid) try: print("start to count timeout; timeout set to be %d \n" % (self.timeout,)) ps.wait() finally: my_timer.cancel() stdout.flush() stderr.flush() stdout.close() stderr.close() if __name__ == "__main__": tsp = TestSubProcess() tsp.run()
總結:
關於p = subprocess.Popen,最好用p.communicate.而不是直接用p.wait(), 由於p.wait()有可能由於子進程往PIPE寫的時候寫滿了,可是子進程尚未結束,致使子進程阻塞,而父進程一直在wait(),致使父進程阻塞。並且p.wait()和p.communicate不能一塊兒用,由於p.communicate裏面也會去調用wait()。