web-dev-qa-db-fra.com

Script bash - stocker stderr dans une variable

J'écris un script pour sauvegarder une base de données. J'ai la ligne suivante:

mysqldump --user=$dbuser --password=$dbpswd  \
   --Host=$Host $mysqldb | gzip > $filename

Je veux assigner le stderr à une variable, afin qu'il m'envoie un e-mail m'informant de ce qui s'est passé en cas de problème. J'ai trouvé des solutions pour rediriger stderr vers stdout, mais je ne peux pas le faire car la stdout est déjà envoyée (via gzip) dans un fichier. Comment puis-je stocker séparément stderr dans un résultat $ variable?

47
thornate

Essayez de rediriger stderr vers stdout et d'utiliser $() pour capturer cela. En d'autres termes:

VAR=$((your-command-including-redirect) 2>&1)

Puisque votre commande redirige stdout quelque part, elle ne devrait pas interférer avec stderr. Il pourrait y avoir une façon plus propre de l'écrire, mais cela devrait fonctionner.

Modifier:

Cela fonctionne vraiment. Je l'ai testé:

#!/bin/bash                                                                                                                                                                         
BLAH=$((
(
echo out >&1
echo err >&2
) 1>log
) 2>&1)

echo "BLAH=$BLAH"

imprimera BLAH=err et le fichier log contient out.

81
Adam Crume

Pour toute commande générique dans Bash, vous pouvez faire quelque chose comme ceci:

{ error=$(command 2>&1 1>&$out); } {out}>&1

La sortie régulière apparaît normalement, tout ce qui est envoyé à stderr est capturé dans $ error (citez-le comme "$ error" lorsque vous l'utilisez pour conserver les retours à la ligne). Pour capturer stdout dans un fichier, ajoutez simplement une redirection à la fin, par exemple:

{ error=$(ls /etc/passwd /etc/bad 2>&1 1>&$out); } {out}>&1 >output

Le décomposer, lire de l'extérieur vers l'intérieur, cela:

  • crée une description de fichier $ out pour le bloc entier, dupliquant stdout
  • capture la sortie standard de la commande entière dans $ error (mais voir ci-dessous)
  • la commande elle-même redirige stderr vers stdout (qui est capturée ci-dessus) puis stdout vers la stdout d'origine depuis l'extérieur du bloc, donc seul le stderr est capturé
18
Joat

Vous pouvez enregistrer la référence stdout avant qu'elle ne soit redirigée dans un autre numéro de fichier (par exemple 3), puis rediriger stderr vers celui-ci:

result=$(mysqldump --user=$dbuser --password=$dbpswd  \
   --Host=$Host $mysqldb 3>&1 2>&3 | gzip > $filename)

Alors 3>&1 redirigera le fichier numéro 3 vers stdout (notez que c'est avant que stdout soit redirigé avec le tube). Ensuite 2>&3 redirige stderr vers le fichier numéro 3, qui est maintenant le même que stdout. Enfin stdout est redirigé en étant alimenté dans un tube, mais cela n'affecte pas les numéros de fichiers 2 et 3 (notez que la redirection de stdout depuis gzip n'est pas liée aux sorties de la commande mysqldump).

Edit: Mise à jour de la commande pour rediriger stderr à partir de la commande mysqldump et non gzip, j'ai été trop rapide dans ma première réponse.

5
hlovdal

dd écrit à la fois stdout et stderr:

$ dd if=/dev/zero count=50 > /dev/null 
50+0 records in
50+0 records out

les deux flux sont indépendants et réorientables séparément:

$ dd if=/dev/zero count=50 2> countfile | wc -c
25600
$ cat countfile 
50+0 records in
50+0 records out
$ mail -s "countfile for you" thornate < countfile

si vous aviez vraiment besoin d'une variable:

$ variable=`cat countfile`
0
msw