web-dev-qa-db-fra.com

Tuyau uniquement STDERR à travers un filtre

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 ────┘
65
Martin DeMello

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:

  1. Enregistrez d'abord la sortie standard sous & 3 (& 1 est dupé en 3)
  2. Envoyez ensuite stdout à stderr (& 2 est dupé en 1)
  3. Envoyer stderr à & 3 (stdout) (& 3 est dupé en 2)
  4. close & 3 (& - est dupé en 3)
57
Paul Rubel

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
21
solidsnack

TL; DR:

$ 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 .


Explication

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.

12
Tom Hale

TL; DR: (bash et zsh)

$ 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 .

9
Tom Hale

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.

8
Pecunifex

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.

5
signine

Jetez un œil aux canaux nommés:

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