Le shell POSIX (sh) redirige stderr vers stdout et capture stderr et stdout dans des variables

Je voudrais rediriger stderr vers stdout afin qu'un terminal les imprime tous les deux lors de l'exécution d'une commande, mais je voudrais également les capturer tous les deux dans des variables distinctes. J'ai réussi à y parvenir dans Bash (version 4.4.20(1)-release):

#!/bin/bash
echo "terminal:"
{ err="$(find / -maxdepth 2 -iname 'tmp' -type d 2>&1 1>&3 3>&- | tee /dev/stderr)"; ec="$?"; } 3>&1 | tee /dev/fd/4 2>&1; out=$(cat /dev/fd/4)
echo "stdout:" && echo "$out"
echo "stderr:" && echo "$err"

cela donne souhaité:

terminal:
find: '/root': Permission denied
/tmp
/var/tmp
find: '/lost+found': Permission denied
stdout:
/tmp
/var/tmp
stderr:
find: '/root': Permission denied
find: '/lost+found': Permission denied

mais j'ai un problème pour convertir ce script en POSIX sh/bin/sh

#!/bin/sh
echo "terminal:"
{ err="$(find / -maxdepth 2 -iname 'tmp' -type d 2>&1 1>&3 3>&- | tee /dev/stderr)"; ec="$?"; } 3>&1 | tee /dev/fd/4 2>&1; out=$(cat /dev/fd/4)
echo "stdout:" && echo "$out"
echo "stderr:" && echo "$err"

donne:

terminal:
tee: /dev/fd/4: No such file or directory
find: '/root': Permission denied
/tmp
/var/tmp
find: '/lost+found': Permission denied
cat: /dev/fd/4: No such file or directory
stdout:
stderr:

/dev/fd/4n'existe pas, et il n'y a pas /proc/self/fd/4non plus.

Comment faire en sorte que ce script fonctionne comme un script shell POSIX ?


Solution du problème

Eh bien, je le fais dans le ashshell de busybox et mes options sont plutôt limitées, mais c'est ce que j'ai trouvé qui fonctionne.

tmpfile=/tmp/some_prefix_stderr.$$
out=$(my_cmd) 2>$tmpfile
result=$?
err=$(cat $tmpfile)
rm $tmpfile

Ce n'est pas le plus beau, mais ça marche. En général, remplacez some_prefixpar quelque chose de spécifique pour éviter les collisions, mais le simple fait d'avoir le PID là-bas résoudra cela à moins que vous n'exécutiez plusieurs threads.

Si vous pouvez vous attendre à ce que votre programme sorte non nul lorsqu'il y a quelque chose à afficher dans stderr, vous pouvez le simplifier un peu:

tmpfile=/tmp/some_prefix_stderr.$$
if! out=$(my_cmd) 2>$tmpfile; then
# specific behavior here
cat $tmpfile >&2
fi
rm $tmpfile

Tout cela est conforme à POSIX. Assurez-vous simplement de nettoyer votre fichier temporaire.

Commentaires

Posts les plus consultés de ce blog

Erreur Symfony : "Une exception a été levée lors du rendu d'un modèle"

Détecter les appuis sur les touches fléchées en JavaScript

Une chaîne vide donne "Des erreurs ont été détectées dans les arguments de la ligne de commande, veuillez vous assurer que tous les arguments sont correctement définis"