bash: utilice una variable para almacenar la redirección stderr | stdout

17

¿Hay alguna manera de redirigir stdout y stderr a través de variables como agregar opciones de comando en el script?

Por ejemplo tengo un script:

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

Puedo ver que OPT se reemplaza -psin problemas y bash lo interpreta como una opción. Pero la redirección se interpreta como el nombre de los directorios.

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

¿Hay alguna manera de decir bash, que $ VAR es redirección, no nombres de directorios?

PD. Puede ser que estoy en el camino equivocado, pero quiero hacer una salida detallada o no detallada de mi script. Pero necesito algo de salida incluso en modo no detallado, por lo tanto, no puedo simplemente redirigir stdout y stderr completos, solo desde algunos comandos dentro de mi script.

prisa
fuente

Respuestas:

18

Otra solución podría ser la siguiente:

#!/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

Luego agregue 1>&3 2>&4solo a los comandos de los que desea ver la salida.

enzotib
fuente
Excelente. Esto es exactamente lo que estaba buscando. Gracias.
prisa el
He escrito un artículo para explicar cómo funciona esto
Transang
5

El "cómo" se ha explicado bien en otras respuestas; Quiero abordar el "por qué" el código del OP no funciona.

La nota clave es que las redirecciones de salida se marcan antes de la expansión variable. Las redirecciones se realizan realmente después de la expansión de la variable (de ahí que se pueda redirigir la salida a un nombre de archivo que se almacena en una variable), pero el shell identifica las redirecciones para el procesamiento posterior antes de que las variables se expandan.

En otras palabras, una vez que se expanden las variables, es "demasiado tarde" para un carácter de redireccionamiento (< o >, etc.), porque el shell ya ha identificado qué partes de la cadena de comandos manejará como redireccionamientos.

Para leer más, vea los pasos 1 y 3 enumerados a continuación:

LESS='+/^SIMPLE COMMAND EXPANSION' man bash
Comodín
fuente
3

No se interpreta como un "nombre de directorio", >se cita, por lo que se trata literalmente (más específicamente, está enviando la cadena >dev/null 2>&1. Su única forma de evitar esto es usandoeval o generar un nuevo shell.

En cuanto a su problema "detallado" aludido en su pregunta, simplemente haga esto en su lugar:

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

Tiene muchos espacios en sus variables, que no se evaluarán correctamente. Querrá usar evalpara configurar eso.

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

Esto permitirá que $ OPT se divida en dos argumentos ( -py -v) en lugar de uno ( -p -v) y lo mismo con $ TEST. También se cambió para usar, /dev/nullya que es muy poco probable que tenga un devdirectorio en el directorio actual.

Arcege
fuente
0

La respuesta de enzotib usa una buena magia de descriptores de archivos, pero una solución más simple sería usar evalla línea (que sin embargo agrega una sobrecarga del proceso):

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