Кодировка по умолчанию для python для stderr? - PullRequest
7 голосов
/ 12 марта 2009

У меня есть шумный скрипт на python, который я хочу заставить замолчать, направив его вывод stderr в / dev / null (используя bash BTW).

Вроде так:

python -u parse.py  1> /tmp/output3.txt 2> /dev/null

но он быстро выходит преждевременно. Хм. Я не вижу следа, потому что, конечно, это происходит с stderr. Он работает шумно и нормально, если я не направляю stderr куда-либо.

Итак, давайте попробуем перенаправить его в файл куда-нибудь, а не в / dev / null, и посмотрим, что он выводит:

python -u parse.py  1> /tmp/output3.txt 2> /tmp/foo || tail /tmp/foo

Traceback (most recent call last):
  File "parse.py", line 79, in <module>
    parseit('pages-articles.xml')
  File "parse.py", line 33, in parseit
    print >>sys.stderr, "bad page title", page_title
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

Итак, генерируемый stderr содержит utf8, и по какой-то причине python отказывается печатать non-ascii при перенаправлении, даже если он направляется в / dev / null (хотя, конечно, python этого не знает) .

Как я могу заставить замолчать stderr скрипта на python, даже если он содержит utf8? Есть ли способ сделать это без перезаписи каждого отпечатка в stderr в этом скрипте?

Ответы [ 3 ]

5 голосов
/ 12 марта 2009

Вы можете отключить stderr, привязав его к пользовательскому устройству записи:

#!/usr/bin/env python
import codecs, sys

class NullWriter:
    def write(self, *args, **kwargs):
        pass

if len(sys.argv) == 2:
   if sys.argv[1] == '1':
      sys.stderr = NullWriter()
   elif sys.argv[1] == '2':
      #NOTE: sys.stderr.encoding is *read-only* 
      #      therefore the whole stderr should be replaced
      # encode all output using 'utf8'
      sys.stderr = codecs.getwriter('utf8')(sys.stderr)

print >>sys.stderr, u"\u20AC" # euro sign
print "ok"

Пример:

$ python silence_stderr.py
Traceback (most recent call last):
  File "silence_stderr.py", line 11, in <module>
    print >>sys.stderr, u"\u20AC"
UnicodeEncodeError: 'ascii' codec can't encode character u'\u20ac' in position 0: ordinal not in range(128)

Глушитель stderr:

$ python silence_stderr.py 1
ok

Кодированный stderr:

$ python silence_stderr.py 2
€
ok

ПРИМЕЧАНИЕ : у меня есть вышеупомянутые выходные данные внутри emacs, поэтому для эмуляции его в терминале вы можете сделать:

$ python ... 2>out.txt
$ cat out.txt

ПРИМЕЧАНИЕ : Внутри консоли Windows (после chcp 65001, который переключается на 'utf-8' и со шрифтом TrueType (Lucida Console)) у меня странные результаты:

C:\> python silence_stderr.py 2
Traceback (most recent call last):
  File "silence_stderr.py", line 14, in <module>
    print >>sys.stderr, u"\u20AC" # euro sign
  File "C:\pythonxy\python\lib\codecs.py", line 304, in write
    self.stream.write(data)
IOError: [Errno 13] Permission denied

Если шрифт неверный, исключение не возникает, но вывод неправильный.

Perl работает для шрифта TrueType:

C:\> perl  -E"say qq(\x{20ac})"
Wide character in print at -e line 1.
€

Перенаправление работает, хотя:

C:\>python silence_stderr.py 2 2>tmp.log
ok
C:\>cat tmp.log
€
cat: write error: Permission denied

комментарий

С codecs.getwriter документация:

Посмотрите кодек для данного кодировать и вернуть свой StreamWriter класс или фабричная функция. Поднимает LookupError в случае кодировки не может быть найден.

Упрощенное представление:

class UTF8StreamWriter:
    def __init__(self, writer):
        self.writer = writer
    def write(self, s):
        self.writer.write(s.encode('utf-8'))

sys.stderr = UTF8StreamWriter(sys.stderr)
4 голосов
/ 12 марта 2009

Когда stderr не перенаправлен, он принимает кодировку вашего терминала. Это все уходит, когда вы перенаправляете это все же. Вам нужно будет использовать sys.stderr.isatty (), чтобы определить, перенаправлен ли он, и правильно ли кодировать.

2 голосов
/ 12 марта 2009

Вы также можете просто кодировать строку как ASCII, заменяя символы Юникода, которые не отображаются Тогда вам не нужно беспокоиться о том, какой у вас терминал.

asciiTitle = page_title.encode("ascii", "backslashreplace")
print >>sys.stderr, "bad page title", asciiTitle

Это заменяет символы, которые не могут быть закодированы с помощью обратной косой черты, т.е. Есть также некоторые другие варианты замены, описанные здесь:

http://docs.python.org/library/stdtypes.html#str.encode

...