python中執行命令行的方法 (二)交互式

概述

  在上一篇文章(http://blog.csdn.net/styshoo/article/details/48755905)中,咱們瞭解了python執行命令行的幾種方法。然而,以前介紹的方法中,卻沒法進行交互式地調用,即命令只能一次執行,執行以後就結束了。若是咱們須要交互式地調用,如調用一個命令,在此過程當中間斷性的輸入輸出,那麼以前的方法就不適合使用了。想要達到這個目的,就必須使用管道了。
  在python中,有兩種使用管道的方法,一種是前文中提到的popen,不過該函數已經在2.6版本中被建議棄用了,取代它的就是立刻會提到的第二種方法:subprocess模塊。python

使用

  subprocess模塊是python2.4新加入的模塊,而加入該模塊的目的,正是爲了替代上文中提到的那些模塊或函數:os.system、os.spawn、os.popen、popen2和commands等。subprocess模塊使用管道是經過該模塊的Popen類來實現。shell

class subprocess.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)

  建立該類時就生成了一個管道,要經過管道交互,可再直接經過該管道的stdout和stdin對象來實現。然而,默認狀況下,subprocess只在子進程結束時纔會讀取一次標準輸出。所以,咱們須要將標準輸出改成非阻塞的模式。這裏,咱們以在「sh」下執行ls命令來模擬(真實狀況下確定不會這麼簡單啦)。函數

import subprocess
import fcntl, os
import time

pipe = subprocess.Popen("sh", stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

flags = fcntl.fcntl(pipe.stdout, fcntl.F_GETFL)
fcntl.fcntl(pipe.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)

pipe.stdin.write("ls \n")
pipe.stdin.flush()

time.sleep(2)
out = pipe.stdout.read()

print out

進階

  在上面的代碼中,在從輸出讀數據以前,有一個休眠兩秒的操做。之因此這樣,是由於這裏全部的操做都是針對子進程的,在主線程中並不會等待。若是沒有這個休眠操做,那麼就會在執行輸入的ls操做同時,就直接嘗試從輸出讀取了,這時,頗有可能會出現讀取異常。試想下,若是向輸入寫的是「sleep 10; ls」這樣的操做,那麼就百分百會出現讀取異常了。
  然而,這種休眠操做並不友好,由於直接寫入休眠時間,而實際狀況中,咱們並不能確認休眠的時間長短。有什麼辦法能夠解決麼?固然有,就是類UNIX系統的epoll操做。這裏我直接寫入代碼,有興趣的同窗能夠參考下reference來了解詳細的狀況。spa

import subprocess
import fcntl, os
import time
import select

pipe = subprocess.Popen("sh", stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

flags = fcntl.fcntl(pipe.stdout, fcntl.F_GETFL)
fcntl.fcntl(pipe.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)

pipe.stdin.write("sleep 10; ls \n")
pipe.stdin.flush()

poll = select.epoll()
poll.register(pipe.stdout.fileno())
epoll_list = poll.poll()
for fd, events in epoll_list:
    if fd == pipe.stdout.fileno() and select.EPOLLIN & events:
        out = pipe.stdout.read()
        print out
相關文章
相關標籤/搜索