Пропускайте только STDERR через фильтр


82

Есть ли способ в bash пропустить STDERR через фильтр перед его объединением с STDOUT? То есть я хочу

STDOUT ────────────────┐
                       ├─────> terminal/file/whatever
STDERR ── [ filter ] ──┘

скорее, чем

STDOUT ────┐
           ├────[ filter ]───> terminal/file/whatever
STDERR ────┘

Ответы:


64

Вот пример, смоделированный после того, как поменять местами дескрипторы файлов в bash . Вывод a.out следующий без префикса «STDXXX:».

STDERR: stderr output
STDOUT: more regular

./a.out 3>&1 1>&2 2>&3 3>&- | sed 's/e/E/g'
more regular
stdErr output

Цитата из приведенной выше ссылки:

  1. Сначала сохраните стандартный вывод как & 3 (& 1 дублируется в 3)
  2. Затем отправьте stdout в stderr (& 2 дублируется в 1)
  3. Отправить stderr в & 3 (stdout) (& 3 дублируется в 2)
  4. закрыть & 3 (& - дублируется на 3)

1
У меня это не работает. Наконец я заставил его работать 3>&2 2>&1 1>&3-.
— aleung

3
Внимание : предполагается, что FD 3 не используется, и не отменяет замену файловых дескрипторов 1 и 2, поэтому вы не можете передать это еще одной команде. См. Этот ответ для получения дополнительных сведений и способов решения проблемы. Более чистый синтаксис для {ba, z} sh см. В этом ответе .
— Tom Hale

25

TL; DR:

$ cmd 2> >(stderr-filter >&2)

Пример:

% cat /non-existant 2> >(tr o X >&2)
cat: /nXn-existant: NX such file Xr directXry
%

Это будет работать как в bash, так и в zsh. В наши дни Bash в значительной степени повсеместен, однако, если вам действительно нужно (действительно грубое) решение для POSIX sh, посмотрите здесь .


Объяснение

Безусловно, самый простой способ сделать это - перенаправить STDERR с помощью замены процесса :

Подстановка процесса позволяет ссылаться на вход или выход процесса с использованием имени файла. Он принимает форму

>(list)

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

Таким образом, при подстановке процесса вы получаете имя файла.

Так же, как вы могли бы сделать:

$ cmd 2> filename

ты можешь сделать

$ cmd 2> >(filter >&2)

>&2Редирект - х filter«s STDOUT обратно к первоначальному STDERR.


1
Существует предостережение, связанное с этим ответом: bash не ожидает завершения замещенного процесса , в то время как FD жонглирует для обмена местами 1 и 2. Это может быть важно. Я был обожжен этим выполнением exec 2> >(while .. read .. echo)сценария, работающего как служба systemd. journald захватывает fd2 службы и определяет уровень журнала по префиксу «<N>»: добавление <2> к строкам вывода, и вы получаете уровень ОШИБКИ, назначенный записям журнала. Но иногда systemd был быстрее, чтобы убить всю группу процессов при выходе из основной, прежде чем она успела записать свое последнее, самое важное сообщение!
— kkm

Хорошее простое решение. Предупреждение: будьте осторожны, используя это как часть работы cron; замена процесса недоступна в некоторых оболочках, используемых cron.
— Куинн Комендант

24

Наивное использование подстановки процесса, кажется, позволяет фильтровать stderrотдельно от stdout:

:; ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 )
out
e:err

Обратите внимание , что stderrвыходит на stderrи stdoutна stdout, которые мы можем видеть, завернув все это в другой субоболочке и перенаправление файлы oиe

( ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 ) ) 1>o 2>e

Почему :; с начала? Я пробовал волшебную линию без нее, и, похоже, это не имеет значения.
— Koshmaar

1
Некоторые люди используют его $как командную строку. Тогда они часто пишут оболочки примеры , как: $ cat /var/log/syslog | fgrep .... Однако эту строку нельзя скопировать из-за расширения $. :;выглядит как приглашение, но, по сути, не работает с оболочкой; так что вы можете безопасно выбрать и вставить всю строку.
— solidsnack

23
Хорошо, но вы можете просто опустить:; и строка также была бы копируемой :) Мне:; не выглядит как приглашение, это не делает пример более ясным (отделяет команды от вывода), но сбивает с толку. Хотя я понимаю вашу точку зрения и не будем обсуждать синтаксис / соглашения.
— Koshmaar

15

TL; DR: (bash и zsh)

$ cmd 2> >(stderr-filter >&2)

Пример:

% cat /non-existant 2> >(tr o X >&2)
cat: /nXn-existant: NX such file Xr directXry
%

Многие ответы в сети StackExchange имеют форму:

cat /non-existant 3>&1 1>&2 2>&3 3>&- | sed 's/e/E/g'

У этого есть встроенное предположение: этот файловый дескриптор 3 не используется для чего-то еще.

Вместо этого используйте именованный файловый дескриптор и {ba,z}shвыделите следующий доступный файловый дескриптор> = 10:

cat /non-existant {tmp}>&1 1>&2 2>&$tmp {tmp}>&- | sed 's/e/E/g'

Обратите внимание, что именованные файловые дескрипторы не поддерживаются POSIX. sh .

Другая проблема, связанная с вышеизложенным, заключается в том, что команду нельзя передать по конвейеру другим командам без повторной замены STDOUT и STDERR на их исходные значения.

Чтобы разрешить последующий конвейер в POSIX sh(и все еще предполагая, что FD 3 не используется), это усложняется :

(cmd 2>&1 >&3 3>&- | stderr-filter >&2 3>&-) 3>&1

Итак, учитывая предположение и грубый синтаксис этого, вам, вероятно, будет лучше использовать более простой bash/ zshсинтаксис, показанный в TL; DR выше и объясненный здесь .


8

Я считаю, что использование замены процесса bash легче запомнить и использовать, поскольку оно почти дословно отражает исходное намерение. Например:

$ cat ./p
echo stdout
echo stderr >&2
$ ./p 2> >(sed -e 's/s/S/') | sed 's/t/T/'
sTdout
STderr

использует первую команду sed в качестве фильтра только для stderr, а вторую команду sed для изменения объединенного вывода.

Обратите внимание, что пробел после 2> является обязательным для правильного анализа команды.


5

Последняя часть этой страницы Advanced Bash Scripting Guide - «перенаправление только stderr на канал».

# Перенаправление в канал только stderr.

exec 3>&1                              # Save current "value" of stdout.
ls -l 2>&1 >&3 3>&- | grep bad 3>&-    # Close fd 3 for 'grep' (but not 'ls').
#              ^^^^   ^^^^
exec 3>&-                              # Now close it for the remainder of the script.

# Спасибо, SC

Это может быть то, что вы хотите. Если нет, то какая-то другая часть ABSG сможет вам помочь, это отлично.


Мы несколько не решаемся рекомендовать ABSG в качестве справочного материала, поскольку он сочетает в себе документацию, предписания и мнения без четкого обозначения различий. Некоторые разделы также имеют сомнительное содержание, хотя тот, на который вы ссылаетесь, кажется нормальным.
— Tripleee 07

3

Взгляните на именованные каналы:

$ mkfifo err
$ cmd1 2>err |cat - err |cmd2

не cat - errнарушит чередование stdout и stderr?
— Мартин ДеМелло 01

@Martin - это зависит от обстоятельств. Если выводятся буферы cmd1, cat или cmd2, то вы можете увидеть вывод вне последовательности.
— моб
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.