2012-05-07 3 views
8

Я пытаюсь вызвать скрипт оболочки в python, но он продолжает сообщать об ошибке поврежденного канала (результат в порядке, но я не хочу видеть сообщение об ошибке в STDERR). Я точно определили причину, и она может быть воспроизведена в следующем фрагменте:Python: subprocess.call broken pipe

subprocess.call('cat /dev/zero | head -c 10 | base64', shell=True)

AAAAAAAAAAAAAA ==

кошки: написать об ошибке: Broken трубы

/dev/zero бесконечный поток, но head -c 10 только считывает 10 байтов из него и выходит, тогда кошка получит SIGPIPE из-за того, что сверстник закрыл трубу. Когда я запускаю команду в оболочке, нет сообщения об ошибке сбойной трубы, но почему это показывает python?

+4

ошибка уходит, когда вы пропустите [uuoc] (https://en.wikipedia.org/wiki/ Cat_% 28Unix% 29 # Useless_use_of_cat): 'subprocess.call ('head -c 10

+2

@larsmans: вы можете поставить это как ответ –

+0

@ChrisMorgan: Я предпочитаю ваш ответ. –

ответ

2

В этом тривиальном случае, по крайней мере, вы ничего не набираете, используя команды оболочки —, и вы теряете переносимость и скорость.

Python 2 Код:

>>> import base64 
>>> base64.b64encode(open('/dev/zero', 'rb').read(10)) 
'AAAAAAAAAAAAAA==' 
>>> base64.b64encode('\0' * 10) 
'AAAAAAAAAAAAAA==' 

В Python 3 (код также будет работать в 2.6+, хотя он будет возвращать str, а не bytes экземпляров):

>>> import base64 
>>> base64.b64encode(open('/dev/zero', 'rb').read(10)) 
b'AAAAAAAAAAAAAA==' 
>>> base64.b64encode(b'\0' * 10) 
b'AAAAAAAAAAAAAA==' 

В каждом случае, первый пример сохраняет использование /dev/zero (сам по себе не переносимый, но неважно), второй производит эффект, хотя я думаю, что это не то, что вы хотите конкретно?

+0

+1. '' \ x00'' также можно записать '' \ 0'' (или 'b '\ 0'' в Python 3.x). –

+1

'\ 0' короче' \ x00', я полагаю ... –

+0

Думаю, я предпочитаю ответ субдира, который напрямую затрагивает вопрос и объясняет проблему. –

6

Действие по умолчанию для сигнала SIGPIPE - это прекращение программы. Интерпретатор Python меняет его на SIG_IGN, чтобы иметь возможность сообщать об ошибках разбитых труб в программу в виде исключений.

Когда вы выполняете cat ... |head ... в оболочке, cat имеет обработчик SIGPIPE по умолчанию, а ядро ​​ОС просто завершает его на SIGPIPE.

При выполнении cat с помощью subprocess это унаследованный обработчик SIGPIPE от своего родителя (питон переводчик), SIGPIPE просто игнорируются, и cat обрабатывает самое ошибку, проверяя errno переменного и печати сообщения об ошибке.

Чтобы избежать сообщений об ошибках от cat вы можете использовать preexec_fn аргумент subprocess.call:

from signal import signal, SIGPIPE, SIG_DFL 
subprocess.call(
    'cat /dev/zero | head -c 10 | base64', 
    shell = True, 
    preexec_fn = lambda: signal(SIGPIPE, SIG_DFL) 
) 
+0

Отлично, спасибо большое за это, просто решил мою проблему с помощью скрипта оболочки с помощью cat (и я уверен, что это не uuoc, может быть, это еще один вопрос!). –

+0

действительно отличное объяснение и решение. –

+0

Отлично! Исправлена ​​ошибка tr: broken pipe, вызванная вызовом сценария bash с subprocess.call() – Hanynowsky