bash: utiliser une variable pour stocker la redirection stderr | stdout

17

Existe-t-il un moyen de rediriger stdout et stderr via une variable comme l'ajout d'options de commande dans le script?

Par exemple, j'ai un script:

#!/bin/bash -x
TEST=">/dev/null 2>&1"
OPT='-p -v'
mkdir $OPT 123/123/123 $TEST

Je peux voir que OPT est remplacé par -psans aucun problème et bash l'interprète en option. Mais la redirection interprète comme nom de répertoire.

$ ./test.sh 
+ TEST='>/dev/null 2>&1'
+ OPT='-p -v'
+ mkdir -p -v 123/123/123 '>/dev/null' '2>&1'
mkdir: created directory `123/123'
mkdir: created directory `123/123/123'
mkdir: created directory `>/dev'
mkdir: created directory `>/dev/null'
mkdir: created directory `2>&1'

Existe-t-il un moyen de dire bash, que $ VAR est une redirection, pas un nom de répertoire.

PS. Je me trompe peut-être, mais je veux faire une sortie verbeuse ou non verbeuse facultative à partir de mon script. Mais j'ai besoin d'une sortie même en mode non verbeux, donc je ne peux pas simplement rediriger stdout et stderr entiers, uniquement à partir de certaines commandes à l'intérieur de mon script.

se ruer
la source

Réponses:

18

Une autre solution pourrait être la suivante:

#!/bin/bash

verbose=0

exec 3>&1
exec 4>&2

if ((verbose)); then
  echo "verbose=1"
else
  echo "verbose=0"
  exec 1>/dev/null
  exec 2>/dev/null
fi

echo "this should be seen if verbose"
echo "this should always be seen" 1>&3 2>&4

Ajoutez ensuite 1>&3 2>&4uniquement aux commandes dont vous souhaitez voir la sortie.

enzotib
la source
Génial. C'est exactement ce que j'ai observé. Je vous remercie.
précipiter le
J'ai écrit un article pour expliquer comment cela fonctionne
transang
5

Le «comment» a été bien expliqué dans d'autres réponses; Je veux aborder le «pourquoi» le code de l'OP ne fonctionne pas.

La note clé est que les redirections de sortie sont marquées avant l' expansion des variables. Les redirections sont en fait effectuées après l'expansion des variables (d'où la raison pour laquelle vous pouvez rediriger la sortie vers un nom de fichier qui est stocké dans une variable), mais le shell identifie les redirections pour un traitement ultérieur avant que les variables ne soient développées.

En d'autres termes, une fois que les variables sont développées, il est "trop ​​tard" pour qu'un caractère de redirection ( <ou >, etc.) soit pris en compte, car le shell a déjà identifié les parties de la chaîne de commande qu'il va gérer comme redirections.

Pour plus d'informations, consultez les étapes 1 et 3 répertoriées sous:

LESS='+/^SIMPLE COMMAND EXPANSION' man bash
Caractère générique
la source
3

Il ne l'interprète pas comme un "nom de répertoire", >est cité, donc il est traité littéralement (plus précisément, vous envoyez la chaîne >dev/null 2>&1. Votre seul moyen de contourner cela est d'utiliser evalou de générer un nouveau shell.

Quant à votre problème "verbeux" évoqué dans votre question, faites-le plutôt à la place:

verbose=1
if (( verbose )); then
    mkdir -v -p /foo
else
    mkdir -p /foo > /dev/null 2>&1
fi
Chris Down
la source
1

Vous avez beaucoup d'espaces dans vos variables, qui ne seront pas évalués correctement. Vous voudrez utiliser evalpour configurer cela.

#!/bin/bash -x
TEST=">/dev/null 2>&1"
OPT='-p -v'
eval mkdir $OPT 123/123/123 $TEST

Cela permettra à $ OPT d'être divisé en deux arguments ( -pet -v) au lieu d'un ( -p -v) et le même avec $ TEST. Également modifié pour utiliser /dev/nullcar il est très peu probable que vous ayez un devrépertoire dans le répertoire actuel.

Arcege
la source
0

La réponse d'Enzotib utilise une bonne magie de descripteur de fichier, mais une solution plus simple serait d'utiliser simplement evalla ligne (ce qui ajoute cependant une surcharge de processus):

$ rm /tmp/foo
$ ll /tmp/foo
ls: cannot access /tmp/foo: No such file or directory
$ FOO=">/tmp/foo"
$ date $FOO
date: invalid date '>/tmp/foo'
$ cat /tmp/foo
cat: /tmp/foo: No such file or directory
$ eval date $FOO
$ cat /tmp/foo
Thu Dec 13 14:08:17 EST 2018
Joe Casadonte
la source