Труба только STDERR через фильтр - PullRequest
64 голосов
/ 01 сентября 2010

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

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

вместо

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

Ответы [ 7 ]

56 голосов
/ 01 сентября 2010

Вот пример, смоделированный после , как поменять дескрипторы файлов в 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 (стандартный вывод) (& 3 дублируется на 2)
  4. закрыть & 3 (& - дублируется на 3)
20 голосов
/ 26 ноября 2012

Наивное использование замещения процесса, по-видимому, позволяет фильтровать 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
10 голосов
/ 30 сентября 2018

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 STDOUT обратно на исходный STDERR.

8 голосов
/ 30 августа 2012

Мне кажется, что замещение и использование процесса 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 голосов
/ 30 сентября 2018

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 выше и объяснен здесь .

5 голосов
/ 01 сентября 2010

Последняя часть этой страницы Руководства по расширенному написанию сценариев Bash "перенаправляет только 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.

# Спасибо, С.С.

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

3 голосов
/ 01 сентября 2010

Посмотрите на именованные каналы:

$ mkfifo err
$ cmd1 2>err |cat - err |cmd2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...