Python 的標準輸出作爲管道輸入出現 Broken pipe 錯誤?

今天寫 Python2 程序遇到一個問題,爲說明這個問題,寫一個演示程序,它的代碼如下:

import sys

for line in sys.stdin:
    print line.strip('\n')

該演示程序的功能就是從標準輸入讀取內容輸出到標準輸出,當以如下方式使用時程序會報錯:

ps -elf | python test_pipe.py | head -n 10

報錯內容如下:

Traceback (most recent call last):
  File "test_pipe.py", line 6, in <module>
    print line.strip('\n')
IOError: [Errno 32] Broken pipe

而將head命令換成tail命令則不會報錯。

網上搜索了下,找到了解決方法。解決方法如下:

import sys
from signal import signal, SIGPIPE, SIG_DFL

# 讓 python 忽略 SIGPIPE 信號,並且不拋出異常
signal(SIGPIPE,SIG_DFL) 

for line in sys.stdin:
    print line.strip('\n')

該問題產生的原因是:head程序從管道的一端讀取到足夠的數據後就會關閉管道,而python程序正在管道的另一端寫入,於是python程序就會接收到SIGPIPE信號使程序異常退出。而tail命令是等管道寫入完成後再關閉管道,所以不會有這個問題。

上面簡單的程序有個不足之處:如果標準輸入沒有數據,程序將會一直阻塞。我們希望標準輸入沒有數據時,程序能夠退出。這時可以使用 poll 輪詢標準輸入,在一定的時間內沒有收集到讀事件,則退出程序。代碼如下:

# -*- coding: utf-8 -*-
import os
import sys
import fcntl
import select

from signal import signal, SIGPIPE, SIG_DFL

# 讓 python 忽略 SIGPIPE 信號,並且不拋出異常
signal(SIGPIPE, SIG_DFL)

fd = sys.stdin.fileno()
# 將標準輸入的描述符設置成非阻塞
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK | fl) 

poll = select.poll()
poll.register(fd, select.POLLIN)

while True:

    events = poll.poll(100)
    if not events:
        break

    data = sys.stdin.read()
    if not data:
        break

    sys.stdout.write(data)

參考 : stackoverflow.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章