bash: используйте переменную для хранения перенаправления stderr | stdout

17

Есть ли способ перенаправить stdout и stderr через переменную, например, добавив параметры команды в сценарии?

Например, у меня есть скрипт:

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

Я вижу, что OPT заменяется -pбез каких-либо проблем, и bash интерпретирует его как опцию. Но перенаправление интерпретируется как имя каталога.

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

Есть ли способ сказать bash, что $ VAR - это перенаправление, а не имена dirs.

PS. Может быть, я нахожусь на неправильном пути, но я хочу сделать дополнительный подробный или не многословный вывод из моего сценария. Но мне нужен вывод даже в не подробном режиме, поэтому я не могу просто перенаправить весь stdout и stderr, только с некоторых команд внутри моего скрипта.

порыв
источник

Ответы:

18

Другое решение может быть следующим:

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

Затем добавьте 1>&3 2>&4только те команды, которые вы хотите увидеть вывод.

enzotib
источник
Отлично. Это именно то, что я наблюдал. Спасибо.
Раш
Я написал статью, чтобы объяснить, как это работает
transang
5

«Как» было хорошо объяснено в других ответах; Я хочу ответить на вопрос «почему», код OP не работает.

Суть в том, что перенаправления вывода помечаются до раскрытия переменной. Перенаправления фактически выполняются после раскрытия переменной (следовательно, вы можете перенаправить вывод в имя файла, которое хранится в переменной), но оболочка идентифицирует перенаправления для последующей обработки до раскрытия переменных.

Другими словами, после раскрытия переменных «слишком поздно» рассматривать символ перенаправления ( <или >и т. Д.), Поскольку оболочка уже определила, какие части командной строки она будет обрабатывать как перенаправления.

Для дальнейшего чтения, см. Шаги 1 и 3, перечисленные в разделе:

LESS='+/^SIMPLE COMMAND EXPANSION' man bash
Wildcard
источник
3

Это не интерпретирует его как «имя каталога», > кавычки, поэтому оно трактуется буквально (точнее, вы отправляете строку >dev/null 2>&1. Единственный способ обойти это - использовать evalили порождать новую оболочку.

Что касается вашей «многословной» проблемы, о которой идет речь в вашем вопросе, просто сделайте это вместо этого:

verbose=1
if (( verbose )); then
    mkdir -v -p /foo
else
    mkdir -p /foo > /dev/null 2>&1
fi
Крис Даун
источник
1

В ваших переменных много пробелов, которые не будут оцениваться должным образом. Вы хотите использовать, evalчтобы настроить это.

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

Это позволит разделить $ OPT на два аргумента ( -pи -v) вместо одного ( -p -v) и то же самое с $ TEST. Также изменено для использования, /dev/nullтак как очень маловероятно, что у вас будет devкаталог в текущем каталоге.

Arcege
источник
0

Ответ enzotib использует некоторый хороший файловый дескриптор магии, но более простым решением было бы просто использовать evalстроку (которая делает накладные расходы дополнение процесса, однако):

$ 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
Джо Касадонте
источник