Existe-t-il un moyen, en bash, de diriger STDERR à travers un filtre avant de l'unifier avec STDOUT? Autrement dit, je veux
STDOUT ────────────────┐
├─────> terminal/file/whatever
STDERR ── [ filter ] ──┘
plutôt que
STDOUT ────┐
├────[ filter ]───> terminal/file/whatever
STDERR ────┘
Voici un exemple, modelé d'après comment permuter les descripteurs de fichiers dans bash . La sortie de a.out est la suivante, sans le préfixe '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
Citant le lien ci-dessus:
- Enregistrez d'abord la sortie standard sous & 3 (& 1 est dupé en 3)
- Envoyez ensuite stdout à stderr (& 2 est dupé en 1)
- Envoyer stderr à & 3 (stdout) (& 3 est dupé en 2)
- close & 3 (& - est dupé en 3)
Une utilisation naïve de la substitution de processus semble permettre le filtrage de stderr
séparément de stdout
:
:; ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 )
out
e:err
Notez que stderr
sort sur stderr
et stdout
sur stdout
, que nous pouvons voir en enveloppant le tout dans un autre sous-shell et en redirigeant vers les fichiers o
et e
( ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 ) ) 1>o 2>e
$ cmd 2> >(stderr-filter >&2)
Exemple:
% cat /non-existant 2> >(tr o X >&2)
cat: /nXn-existant: NX such file Xr directXry
%
Cela fonctionnera à la fois dans bash et zsh. Bash est à peu près omniprésent de nos jours, cependant, si vous avez vraiment besoin d'une solution (vraiment noueuse) pour POSIX sh
, alors voir ici .
De loin, la façon la plus simple de le faire est de rediriger STDERR via substitution de processus :
La substitution de processus permet de se référer à l'entrée ou à la sortie d'un processus à l'aide d'un nom de fichier. Il prend la forme de
>(list)
La liste des processus est exécutée de manière asynchrone et son entrée ou sa sortie apparaît sous la forme d'un nom de fichier.
Donc, ce que vous obtenez avec la substitution de processus est un nom de fichier.
Tout comme vous pourriez le faire:
$ cmd 2> filename
tu peux faire
$ cmd 2> >(filter >&2)
Le >&2
STDOUT de filter
de redirection vers le STDERR d'origine.
$ cmd 2> >(stderr-filter >&2)
Exemple:
% cat /non-existant 2> >(tr o X >&2)
cat: /nXn-existant: NX such file Xr directXry
%
De nombreuses réponses sur le réseau StackExchange ont la forme:
cat /non-existant 3>&1 1>&2 2>&3 3>&- | sed 's/e/E/g'
Cela a une hypothèse intégrée: que le descripteur de fichier 3 n'est pas utilisé pour autre chose.
Utilisez plutôt un descripteur de fichier nommé et {ba,z}sh
allouera le prochain descripteur de fichier disponible> = 10:
cat /non-existant {tmp}>&1 1>&2 2>&$tmp {tmp}>&- | sed 's/e/E/g'
Notez que les descripteurs de fichiers nommés ne sont pas pris en charge par POSIX sh
.
L'autre problème avec ce qui précède est que la commande ne peut pas être redirigée vers d'autres commandes sans remettre à nouveau STDOUT et STDERR à leurs valeurs d'origine.
Pour autoriser la tuyauterie en cours dans POSIX sh
(et en supposant toujours que FD 3 ne l'utilise pas), il devient compliqué :
(cmd 2>&1 >&3 3>&- | stderr-filter >&2 3>&-) 3>&1
Donc, étant donné l'hypothèse et la syntaxe noueuse de cela, vous feriez probablement mieux d'utiliser la syntaxe plus simple bash
/zsh
indiquée dans le TL; DR ci-dessus, et expliquée ici .
Je trouve l'utilisation de la substitution de processus bash plus facile à mémoriser et à utiliser car elle reflète l'intention originale presque mot pour mot. Par exemple:
$ cat ./p
echo stdout
echo stderr >&2
$ ./p 2> >(sed -e 's/s/S/') | sed 's/t/T/'
sTdout
STderr
utilise la première commande sed comme filtre sur stderr uniquement et la deuxième commande sed pour modifier la sortie jointe.
Notez que l'espace blanc après 2> est obligatoire pour que la commande soit correctement analysée.
La dernière partie de cette page du Advanced Bash Scripting Guide est "rediriger uniquement stderr vers un canal".
# Redirige uniquement stderr vers un tube.
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.
# Merci, S.C.
C'est peut-être ce que vous voulez. Sinon, une autre partie de l'ABSG devrait pouvoir vous aider, c'est excellent.
Jetez un œil aux canaux nommés:
$ mkfifo err
$ cmd1 2>err |cat - err |cmd2