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'

Чи є спосіб сказати баш, що $ 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лише до команд, з яких ви хочете бачити вихід.


Чудово. Це саме те, за чим я спостерігав. Дякую.
пік

Я написав статтю, щоб пояснити, як це працює
перейшов

5

"Як" було добре пояснено в інших відповідях; Я хочу вирішити "чому" код ОП не працює.

Основне значення полягає в тому, що перенаправлення виводу маркуються перед розширенням змінної. Перенаправлення фактично виконуються після розширення змінної (отже, чому ви можете перенаправити вихід на ім’я файлу, яке зберігається в змінній), але оболонка ідентифікує перенаправлення для подальшої обробки до розширення змінних.

Іншими словами, як тільки змінні змінних розширюються, для перенаправлення символу ( <або >тощо) «занадто пізно» слід вважати, оскільки оболонка вже визначила, які частини командного рядка він обробляє як перенаправлення.

Для подальшого читання див. Кроки 1 та 3, перелічені нижче:

LESS='+/^SIMPLE COMMAND EXPANSION' man bash

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каталог у поточному каталозі.


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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.