Как использовать stdout и stderr io-redirection для получения разумных сообщений об ошибках / предупреждениях из программы?

У меня есть программа, которая выводит на stdout и stderr, но не использует их должным образом. Некоторые ошибки отправляются на стандартный вывод, некоторые - на стандартный вывод, а не ошибки поступают на стандартный вывод и выводят много информации на стандартный вывод. Чтобы исправить это, я хочу сделать конвейер:

  1. Сохраните весь вывод $cmd (как из stderr, так и из stdout) в файл $logfile (не выводите его на экран).
  2. Отфильтруйте все предупреждения и сообщения об ошибках на stderr и stdout (от warning | error до пустой строки) и раскрасьте только «ошибочные» слова (перенаправьте вывод на stderr).
  3. Сохраните вывод шага 2 в файл $logfile:r.stderr.
  4. Выйдите с правильным кодом выхода из команды.

Пока у меня есть это:

$!/bin/zsh
# using zsh 4.2.0
setopt no_multios

# Don't error out if sed or grep don't find a match:
alias -g grep_err_warn="(sed -n '/error\|warning/I,/^$/p' || true)"
alias -g color_err="(grep --color -i -C 1000 error 1>&2 || true)"
alias -g filter='tee $logfile | grep_err_warn | tee $logfile:r.stderr | color_err'

# use {} around command to avoid possible race conditions:
{ eval $cmd } 2>&1 | filter
exit $pipestatus[1]

Я много чего пробовал, но не могу заставить работать. Я прочитал «От Bash к Z Shell», много сообщений и т. Д. В настоящее время у меня возникают следующие проблемы:

  1. В фильтр попадает только стандартный ввод

Примечание: $cmd - это сценарий оболочки, который вызывает двоичный файл с префиксом /usr/bin/time -p. Похоже, это вызывает проблемы с конвейерами, и поэтому я помещаю команду в {…}, весь вывод идет в конвейер.


person dailyglen    schedule 10.06.2011    source источник


Ответы (3)


У меня нет доступного zsh.

Я заметил, что ваше заявление {..}'d неверно.

Перед закрывающим "}" всегда ставится точка с запятой.

Когда я добавил это в bash, я смог убедительно доказать, что stderr перенаправляется на stdout.

Пытаться

{ eval $cmd ; } 2>&1 | filter
# ----------^

Также вы написали

Сохраните весь вывод $ cmd (от stderr и stdout) в файл $ logfile.

Я не вижу упоминания о $ logfile в вашем коде. Вы должны иметь возможность получить весь вывод в файл журнала (при потере специфичности потока stderr) с помощью

yourCommand 2>&1 | tee ${logFile} | ....

Надеюсь, это поможет.

P.S. поскольку вы кажетесь новым пользователем, если вы получили ответ, который поможет вам, не забудьте пометить его как принятый и / или поставить + (или -) в качестве полезного ответа.

person shellter    schedule 10.06.2011
comment
{ eval $cmd } работает под zsh. $logfile появляется в псевдониме filter. - person Gilles 'SO- stop being evil'; 11.06.2011

Не используйте псевдонимы в скриптах, используйте функции (особенно проблемы с глобальными псевдонимами). Не то чтобы вам здесь действительно нужны функции. Вам также не нужен || true (если вы не работаете под set -e, в этом случае вы должны отключить его здесь). В остальном ваш сценарий выглядит нормально; чем он задыхается?

{ eval $cmd } |
tee $logfile |
sed -n '/error\|warning/I,/^$/p' |
tee $logfile:r.stderr |
grep --color -i -C 1000 error 1>&2
exit $pipestatus[1]

Я также не уверен, что вы имели в виду под выражением sed; Я не совсем понимаю ваше требование 2.

person Gilles 'SO- stop being evil'    schedule 10.06.2011
comment
Привет, Жиль, строка sed распечатывает все строки с ошибкой или предупреждением и все строки, которые следуют за ней, до пустой строки. Это выполняет первую половину требования 2. Вторая половина - с 'grep --color ... 1 ›& 2' - person dailyglen; 13.06.2011
comment
Жиль, спасибо за отзыв о || true. У меня было установлено setopt errexit (то же, что и set -e). - person dailyglen; 13.06.2011

Исходный пост был в основном правильным, за исключением оптимизации, сделанной Жилем (чтобы отключить set -e, чтобы || true не понадобились.

#!/bin/zsh
# using zsh 4.2.0
setopt no_multios
#setopt no_errexit # set -e # don't turn this on

{ eval $cmd } 2>&1 |
tee $logfile |
sed -n '/error\|warning/I,/^$/p' |
tee $logfile:r.stderr |
grep --color -i -C 1000 error 1>&2
exit $pipestatus[1]

Что меня смутило, так это смешивание stdout и stderr, что привело к их чередованию, и sed -n '/error\|warning/I,/^$/p' (который распечатывает и выводит сообщение об ошибке || на следующую пустую строку) печатал намного больше, чем ожидалось, что создавало впечатление команда не работала.

person dailyglen    schedule 13.06.2011