it-swarm-ru.tech

Как получить стандартный поток ошибок (stderr)?

Я использую ffmpeg, чтобы получить метаинформацию аудиоклипа. Но я не могу понять это.

    $ ffmpeg -i 01-Daemon.mp3  |grep -i Duration
    FFmpeg version SVN-r15261, Copyright (c) 2000-2008 Fabrice Bellard, et al.
      configuration: --prefix=/usr --bindir=/usr/bin 
      --datadir=/usr/share/ffmpeg --incdir=/usr/include/ffmpeg --libdir=/usr/lib
      --mandir=/usr/share/man --Arch=i386 --extra-cflags=-O2 
      ...

Я проверил, этот вывод ffmpeg направлен на stderr.

$ ffmpeg -i 01-Daemon.mp3 2> /dev/null

Поэтому я думаю, что grep не может прочитать поток ошибок, чтобы поймать совпадающие строки. Как мы можем включить grep для чтения потока ошибок?

Используя ссылку nixCraft , я перенаправил стандартный поток ошибок в стандартный поток вывода, затем сработал grep.

$ ffmpeg -i 01-Daemon.mp3 2>&1 | grep -i Duration
  Duration: 01:15:12.33, start: 0.000000, bitrate: 64 kb/s

Но что, если мы не хотим перенаправлять stderr в stdout?

90
Andrew-Dufresne

Если вы используете bash, почему бы не использовать анонимные каналы, по сути, сокращение от того, что сказал phunehehe:

ffmpeg -i 01-Daemon.mp3 2> >(grep -i Duration)

66
Jé Queue

Ни одна из обычных оболочек (даже zsh) не позволяет использовать каналы, отличные от stdout-stdin. Но все оболочки в стиле Борна поддерживают переназначение дескриптора файла (как в 1>&2). Таким образом, вы можете временно перевести stdout в fd 3 и stderr в stdout, а затем снова поместить fd 3 на stdout. Если stuff выдает какой-то вывод на stdout и вывод на stderr, и вы хотите применить filter к выводу ошибки, оставляя стандартный вывод без изменений, вы можете использовать { stuff 2>&1 1>&3 | filter 1>&2; } 3>&1.

$ stuff () {
  echo standard output
  echo more output
  echo standard error 1>&2
  echo more error 1>&2
}
$ filter () {
  grep a
}
$ { stuff 2>&1 1>&3 | filter 1>&2; } 3>&1
standard output
more output
standard error
49
Gilles 'SO- stop being evil'

Это похоже на "трюк временного файла" в phunehehe, но вместо этого использует именованный канал, позволяя вам получить результаты немного ближе к тому, когда они выводятся, что может быть удобно для длительных команд:

$ mkfifo mypipe
$ command 2> mypipe | grep "pattern" mypipe

В этой конструкции stderr будет направлен на трубу с именем "mypipe". Так как grep был вызван с аргументом файла, он не будет искать STDIN для его ввода. К сожалению, вам все равно придется очистить эту именованную трубу, как только вы закончите.

Если вы используете Bash 4, существует синтаксис ярлыка для command1 2>&1 | command2, который command1 |& command2. Тем не менее, я считаю, что это просто синтаксическое сокращение, вы все еще перенаправляете STDERR в STDOUT.

22
Steven D

Ответы Жиля и Стефана Ласевского хороши, но этот способ проще:

ffmpeg -i 01-Daemon.mp3 2>&1 >/dev/null | grep "pattern"

Я предполагаю, что вы не хотите ffmpeg's стандартный вывод.

Как это работает:

  • сначала трубы
    • ffmpeg и grep запускаются, а стандартный вывод ffmpeg направляется к стандартному grep
  • перенаправления рядом, слева направо
    • stderr для ffmpeg устанавливается равным его стандартному выводу (в настоящее время канал)
    • stfout ffmpeg установлен в/dev/null
11
Mikel

Ниже приведен скрипт, используемый в этих тестах.

Grep может работать только с stdin, поэтому вы должны преобразовать поток stderr в форму, которую Grep может проанализировать.

Обычно stdout и stderr выводятся на ваш экран:

$ ./stdout-stderr.sh
./stdout-stderr.sh: Printing to stdout
./stdout-stderr.sh: Printing to stderr

Чтобы скрыть стандартный вывод, но все же вывести на печать стандартный вывод, сделайте следующее:

$ ./stdout-stderr.sh >/dev/null
./stdout-stderr.sh: Printing to stderr

Но grep не будет работать на stderr! Можно ожидать, что следующая команда подавит строки, содержащие "err", но это не так.

$ ./stdout-stderr.sh >/dev/null |grep --invert-match err
./stdout-stderr.sh: Printing to stderr

Вот решение.

Следующий синтаксис Bash скроет вывод в stdout, но все равно покажет stderr. Сначала мы направляем stdout в/dev/null, затем преобразуем stderr в stdout, потому что каналы Unix будут работать только на stdout. Вы все еще можете получить текст.

$ ./stdout-stderr.sh 2>&1 >/dev/null | grep err
./stdout-stderr.sh: Printing to stderr

(Обратите внимание, что приведенная выше команда отличается затем ./command >/dev/null 2>&1, что является очень распространенной командой).

Вот скрипт, используемый для тестирования. Это печатает одну строку в stdout и одну строку в stderr:

#!/bin/sh

# Print a message to stdout
echo "$0: Printing to stdout"
# Print a message to stderr
echo "$0: Printing to stderr" >&2

exit 0
8
Stefan Lasiewski

Вы можете поменять потоки. Это позволит вам grep исходный стандартный поток ошибок, в то же время получая выходные данные, которые первоначально отправлялись на стандартный вывод в терминале:

somecommand 3>&2 2>&1 1>&3- | grep 'pattern'

Это работает, сначала создав новый дескриптор файла (3), открытый для вывода, и установив его в стандартный поток ошибок (3>&2). Затем мы перенаправляем стандартную ошибку на стандартный вывод (2>&1). Наконец стандартный вывод перенаправляется на исходную стандартную ошибку, а новый дескриптор файла закрывается (1>&3-).

В твоем случае:

ffmpeg -i 01-Daemon.mp3 3>&2 2>&1 1>&3- | grep -i Duration

Тестирование это:

$ ( echo "error" >&2; echo "output" ) 3>&2 2>&1 1>&3- | grep "error"
output
error

$ ( echo "error" >&2; echo "output" ) 3>&2 2>&1 1>&3- | grep -v "error"
output
3
Kusalananda

Когда вы передаете вывод одной команды другой (используя |), вы перенаправляете только стандартный вывод. Так что это должно объяснить, почему

ffmpeg -i 01-Daemon.mp3 | grep -i Duration

не выводит то, что вы хотели (это работает, хотя).

Если вы не хотите перенаправлять вывод ошибок в стандартный вывод, вы можете перенаправить вывод ошибок в файл, а затем выполнить grep позже.

ffmpeg -i 01-Daemon.mp3 2> /tmp/ffmpeg-error
grep -i Duration /tmp/ffmpeg-error
3
phunehehe

bash может перенаправить стандартный вывод в поток стандартного потока через обычный канал - | Он также может перенаправить как stdout, так и stderr в stdin с помощью |&

1
0x00

Мне нравится использовать оболочку rc в этом случае.

Сначала установите пакет (это менее 1 МБ).

Это пример того, как вы должны отбросить stdout и ​​передать stderr в grep в rc:

find /proc/ >[1] /dev/null |[2] grep task

Вы можете сделать это, не выходя из Bash:

rc -c 'find /proc/ >[1] /dev/null |[2] grep task'

Как вы могли заметить, синтаксис прост, что делает это моим предпочтительным решением.

Вы можете указать, какой дескриптор файла вы хотите передать в скобках, сразу после символа канала.

Стандартные файловые дескрипторы нумеруются как таковые:

  • 0: стандартный ввод
  • 1: стандартный выход
  • 2: стандартная ошибка
1
Rolf

Вариант на примере подпроцесса bash:

выведите две строки в stderr и отправьте в файл stderr, grep и отправьте обратно в stdout

(>&2 echo -e 'asdf\nfff\n') 2> >(tee some.load.errors | grep 'fff' >&1)

стандартный вывод:

fff

some.load.errors (например, stderr):

asdf
fff
0
jmunsch