1. 程式人生 > >使用python的subprocess模組遇到的問題及其解決

使用python的subprocess模組遇到的問題及其解決

要求:使用python指令碼,開啟一個子程序並實時讀取並解析其列印(printf())的字串。

首先用linux shell指令碼來做試驗,

寫一個echo-test.sh,迴圈輸出,內容如下:

#!/bin/sh
while :;do
   echo "status:A"
   echo "status:B"
   echo "status:C"
   echo "status:D"
   echo "status:E"
   echo "status:V"    
   echo "status:F"
   sleep 1
done

單獨執行echo-test.sh效果:

$ ./echo-test.sh
status:A
status:B
status:C
status:D
status:E
status:V
status:F
status:A
status:B
status:C
status:D
status:E
status:V
status:F
status:A
status:B
status:C
status:D
status:E
status:V
status:F
^C

再寫一個python指令碼rdstdout.py,讀取並分析echo-test.sh的輸出:
import time
from subprocess import  *
p=Popen("./echo-test.sh",shell=True,stdout=PIPE)
data=p.stdout.readline()
while  data:
    if "status:V" in data:
     print time.time()
     print data
    elif "status:A" in data:
     print time.time()
     print data
    data=p.stdout.readline()

執行下rdstdout.py,
$ python rdstdout.py
1517467252.54
status:A

1517467252.54
status:V

1517467253.54
status:A

1517467253.54
status:V

1517467254.54
status:A

1517467254.54
status:V

^CTraceback (most recent call last):
  File "rdstdout.py", line 14, in <module>
    data=p.stdout.readline()
KeyboardInterrupt

方法可行,初戰告捷!

修改rdstdout.py,

p=Popen("./echo-test.sh",shell=True,stdout=PIPE)   --->

p=Popen("./gps_test.sh 9600",shell=True,stdout=PIPE)

gps_test 讀取並解析gsp module輸出的資料,定位成功輸出status:A,未定位輸出status:V.

單獨執行gps_test,輸出結果,

$ ./gps_test  9600

status:V

total sat:0

status:V

total sat:0

status:V

total sat:0

^C

但執行rdstdout.py,看不到任何輸出,按鍵盤退出程式才看到輸出資訊。

修改p=Popen("./gps_test.sh 9600",shell=True,stdout=PIPE)--->

p=Popen("ping www.baidu.com",shell=True,stdout=PIPE),

可以即時獲取ping輸出的資料。懷疑到printf()有個緩衝,需要在其後加一句fflush(stdout); 這樣修改編譯出新的gps_test,執行python  rdstdout.py可以即時獲取gps_test輸出的資料,

執行 objdump -T /bin/ping |grep fflush
0000000000000000      DF *UND*    0000000000000000  GLIBC_2.2.5 fflush
可以看到ping程式也用到了fflush,應該也是用了類似方法吧。不過需要在每個執行緒的printf()後都要加上fflush(stdout),比較繁瑣,可以在執行主執行緒printf()前加一句setbuf(stdout, NULL),  stdout就沒有buffer可用,每個執行緒的printf()資料都可以即時獲取。

當然也可以用fprintf(stderr,... 代替printf(),也可以解決問題。

這些辦法的共同點就是都需要修改gps_test的原始碼,如果沒有原始碼,這些辦法就不好用了,可以考慮用API hook的方法,具體就不在這裡討論了。