TL;DR:
$ cmd 2> >(stderr-filter >&2)
Příklad:
% cat /non-existant 2> >(tr o X >&2)
cat: /nXn-existant: NX such file Xr directXry
%
To bude fungovat v bash i zsh. Bash je v dnešní době do značné míry všudypřítomný, pokud však opravdu potřebujete (opravdu drsné) řešení pro POSIX sh
, pak se podívejte sem.
Vysvětlení
Zdaleka nejjednodušší způsob, jak toho dosáhnout, je přesměrovat STDERR prostřednictvím substituce procesu:
Substituce procesu umožňuje odkazovat na vstup nebo výstup procesu pomocí názvu souboru. Má podobu
>(list)
Seznam procesů běží asynchronně a jeho vstup nebo výstup se zobrazí jako název souboru.
Takže to, co získáte substitucí procesu, je název souboru.
Stejně jako vy:
$ cmd 2> filename
můžete udělat
$ cmd 2> >(filter >&2)
>&2
přesměrování filter
's STDOUT zpět na původní STDERR.
TL;DR:(bash a zsh)
$ cmd 2> >(stderr-filter >&2)
Příklad:
% cat /non-existant 2> >(tr o X >&2)
cat: /nXn-existant: NX such file Xr directXry
%
Mnoho odpovědí v síti StackExchange má tvar:
cat /non-existant 3>&1 1>&2 2>&3 3>&- | sed 's/e/E/g'
To má vestavěný předpoklad:že deskriptor souboru 3 se nepoužívá pro něco jiného.
Místo toho použijte pojmenovaný deskriptor souboru a {ba,z}sh
přidělí další dostupný deskriptor souboru>=10:
cat /non-existant {tmp}>&1 1>&2 2>&$tmp {tmp}>&- | sed 's/e/E/g'
Všimněte si, že POSIX sh
nepodporuje deskriptory pojmenovaných souborů .
Dalším problémem s výše uvedeným je, že příkaz nelze převést na další příkazy bez opětovného přehození STDOUT a STDERR zpět na jejich původní hodnoty.
Chcete-li povolit další potrubí v POSIX sh
, (a stále za předpokladu, že FD 3 nepoužívá) se to komplikuje:
(cmd 2>&1 >&3 3>&- | stderr-filter >&2 3>&-) 3>&1
Takže vzhledem k předpokladu a drsné syntaxi toho budete pravděpodobně lépe používat jednodušší bash
/zsh
syntaxe zobrazená v TL;DR výše a vysvětlená zde.
praktická ukázka, grepping pouze stderr:
$ ls -l . noexistABC noexistXYZ
ls: cannot access 'noexistABC': No such file or directory
ls: cannot access 'noexistXYZ': No such file or directory
.:
total 4
-rw-rw-r-- 1 frank frank 0 Aug 19 12:26 bar.txt
-rw-rw-r-- 1 frank frank 0 Aug 19 12:26 foo.txt
drwxrwxr-x 2 frank frank 4096 Aug 19 12:26 someFolder
$ ( ls -l . noexistABC noexistXYZ 2>&1 >&3 3>&- | grep ABC >&2 3>&-) 3>&1
.:
ls: cannot access 'noexistABC': No such file or directory
total 4
-rw-rw-r-- 1 frank frank 0 Aug 19 12:26 bar.txt
-rw-rw-r-- 1 frank frank 0 Aug 19 12:26 foo.txt
drwxrwxr-x 2 frank frank 4096 Aug 19 12:26 someFolder
Zde je příklad, modelovaný podle toho, jak zaměnit deskriptory souborů v bash . Výstup a.out je následující, bez předpony '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
Cituji z výše uvedeného odkazu:
- Nejprve uložte stdout jako &3 (&1 je duplikováno do 3)
- Další odešlete stdout na stderr (&2 je duplikováno do 1)
- Odeslat stderr na &3 (stdout) (&3 je duplikováno do 2)
- zavřít &3 (&- je duplikováno do 3)
Zdá se, že naivní použití substituce procesů umožňuje filtrování stderr
odděleně od stdout
:
:; ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 )
out
e:err
Všimněte si, že stderr
vyjde na stderr
a stdout
na stdout
, což můžeme vidět tak, že to celé zabalíme do jiného subshell a přesměrujeme na soubory o
a e
( ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 ) ) 1>o 2>e