bash: use uma variável para armazenar o redirecionamento stderr | stdout

17

Existe alguma maneira de redirecionar stdout e stderr via variável como adicionar opções de comando no script?

Por exemplo, eu tenho um script:

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

Eu posso ver que o OPT é substituído por -psem problemas e o bash o interpreta como opção. Mas o redirecionamento interpreta como o nome dos diretórios.

$ ./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 alguma maneira de dizer bash, que $ VAR é redirecionamento, não um nome de diretório.

PS. Pode ser que eu esteja no caminho errado, mas quero fazer uma saída opcional ou detalhada do meu script. Mas eu preciso de alguma saída, mesmo no modo não detalhado, portanto, não posso apenas redirecionar todo o stdout e o stderr, apenas a partir de alguns comandos dentro do meu script.

pressa
fonte

Respostas:

18

Outra solução pode ser a seguinte:

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

Em seguida, adicione 1>&3 2>&4apenas os comandos dos quais você deseja ver a saída.

enzotib
fonte
Ótimo. Isso é exatamente o que eu assisti. Obrigado.
apressar
Eu escrevi um artigo para explicar como isso funciona
transang 17/11
5

O "como" foi bem explicado em outras respostas; Quero abordar o "por que" o código do OP não funciona.

A nota principal é que os redirecionamentos de saída são marcados antes da expansão da variável. Os redirecionamentos são realmente executados após a expansão da variável (por isso, é possível redirecionar a saída para um nome de arquivo armazenado em uma variável), mas o shell identifica os redirecionamentos para processamento posterior antes que as variáveis ​​sejam expandidas.

Em outras palavras, uma vez que as variáveis ​​são expandidas, é "muito tarde" para que um caractere de redirecionamento ( <ou >etc.) seja considerado, porque o shell já identificou quais partes da cadeia de comando ele irá tratar como redirecionamentos.

Para leitura adicional, consulte as etapas 1 e 3 listadas em:

LESS='+/^SIMPLE COMMAND EXPANSION' man bash
Curinga
fonte
3

Ele não está sendo interpretado como um "nome de diretório", >está sendo citado e, portanto, está sendo tratado literalmente (mais especificamente, você está enviando a string >dev/null 2>&1. Sua única maneira de contornar isso é usar evalou gerar um novo shell.

Quanto ao problema "detalhado" mencionado na sua pergunta, faça o seguinte:

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

Você tem muitos espaços em suas variáveis, que não serão avaliados adequadamente. Você vai querer usar evalpara configurar isso.

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

Isso permitirá que $ OPT seja dividido em dois argumentos ( -pe -v) em vez de um ( -p -v) e o mesmo com $ TEST. Também foi alterado para uso, /dev/nullpois é muito improvável que você tenha um devdiretório no diretório atual.

Arcege
fonte
0

A resposta de enzotib usa alguma mágica descritor de arquivo bom, mas uma solução mais simples seria usar apenas evala linha (que faz sobrecarga processo add, no entanto):

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