Bash: перенаправление на экран или /dev/null в зависимости от флага

Я пытаюсь придумать сценарий для передачи флага silent в bash, чтобы весь вывод был направлен на /dev/null, если он присутствует, и на экран, если его нет.

MWE моего сценария будет:

#!/bin/bash

# Check if silent flag is on.    
if [ $2 = "-s" ]; then
    echo "Silent mode."
    # Non-working line.
    out_var = "to screen"
else
    echo $1
    # Non-working line.
    out_var = "/dev/null"
fi

command1 > out_var

command2 > out_var

echo "End."

Я вызываю скрипт с двумя переменными, первая не имеет значения, а вторая ($2) является фактическим флажком молчания (-s):

./myscript.sh first_variable -s

Очевидно, что строки out_var не работают, но они дают представление о том, чего я хочу: способ направить вывод command1 и command2 либо на экран, либо на /dev/null в зависимости от того, присутствует -s или нет.

Как я мог это сделать?


person Gabriel    schedule 03.09.2014    source источник
comment
В назначениях оболочки нет пробелов вокруг =. При этом и при условии, что вам нужно стандартное расположение вывода по умолчанию, если вы замените "to screen" на /dev/stdout и используете $out_var в перенаправлениях, я считаю, что ваш скрипт должен работать правильно.   -  person Etan Reisner    schedule 03.09.2014
comment
tldp.org/LDP/abs/html/io-redirection.html тоже интересно читать.   -  person Todd A. Jacobs    schedule 03.09.2014
comment
Отличные советы @EtanReisner!   -  person Gabriel    schedule 03.09.2014


Ответы (4)


Вы можете использовать голую команду exec для перенаправления текущей программы без запуска новой.

Следовательно, флаг -s можно обработать примерно так:

if [[ "$1" == "-s" ]] ; then
    exec >/dev/null 2>&1
fi

Следующий полный сценарий показывает, как это сделать:

#!/bin/bash

echo XYZZY

if [[ "$1" == "-s" ]] ; then
    exec >/dev/null 2>&1
fi

echo PLUGH

Если вы запустите его с -s, вы получите XYZZY, но не PLUGH вывод (ну, технически, вы делаете получение PLUGH вывода, но он будет отправлен в /dev/null битное ведро).

Если вы запустите его без -s, вы получите обе строки.

Операторы «до» и «после» echo показывают, что exec действует так, как описано, просто меняет перенаправление для текущей программы, а не пытается выполнить ее повторно.


Кроме того, я предположил, что вы имели в виду «экранировать» как «текущий стандартный вывод», который может быть или не быть фактическим терминальным устройством (например, если оно уже было перенаправлено куда-то еще). Если вам действительно нужно реальное терминальное устройство, это все равно можно сделать (используя, например, /dev/tty), но это было бы необычным требованием.

person paxdiablo    schedule 03.09.2014
comment
Я предложил альтернативную точку зрения, но я думаю, что exec - правильный путь для OP. Однако в более длинных сценариях exec может иметь неожиданные последствия с конвейерами или если кто-то хочет отправить вывод обратно в исходный приемник. Хороший ответ! - person Todd A. Jacobs; 03.09.2014

Есть много вещей, которые могут быть неправильными в вашем сценарии; Я не буду пытаться угадать, поскольку вы не публиковали никаких фактических результатов или ошибок.

Тем не менее, есть пара вещей, которые могут помочь:

  1. Вам нужно выяснить, куда на самом деле идет ваш результат. Стандартный вывод и стандартная ошибка — две разные вещи, и перенаправление одного не обязательно перенаправляет другое.

  2. В Bash вы можете отправлять выходные данные в /dev/stdout или /dev/stderr, поэтому вы можете попробовать что-то вроде:

    # Send standard output to the tty/pty, or wherever stdout is currently going.
    cmd > /dev/stdout
    
    # Do the same thing, but with standard error instead.
    cmd > /dev/stderr
    
  3. Перенаправить стандартную ошибку в стандартный вывод, а затем отправить стандартный вывод в /dev/null. Здесь важен порядок.

    cmd 2>&1 > /dev/null
    

С вашим скриптом могут быть и другие проблемы, но для проблем с оболочкой Bash перенаправления руководство GNU Bash является каноническим источником информации. Надеюсь, поможет!

person Todd A. Jacobs    schedule 03.09.2014

Если вы не хотите перенаправлять весь вывод вашего скрипта, вы можете использовать eval. Например:

$ fd=1
$ eval "echo hi >$a" >/dev/null

$ fd=2
$ eval "echo hi >$a" >/dev/null
hi

Убедитесь, что вы используете двойные кавычки, чтобы переменная была заменена до того, как eval оценит ее.

person whereswalden    schedule 03.09.2014

В вашем случае вам просто нужно было изменить out_var = "to screen" на out_var = "/dev/tty". И используйте это так command1 > $out_var (см. «$», которого вам не хватает)

Я реализовал это так

# Set debug flag as desired
DEBUG=1
# DEBUG=0

if [ "$DEBUG" -eq "1" ]; then
  OUT='/dev/tty'
else
  OUT='/dev/null'
fi

# actual script use commands like this
command > $OUT 2>&1

# or like this if you need
command 2> $OUT

Конечно, вы также можете установить режим отладки из параметра cli, см. Как анализировать аргументы командной строки в Bash?

И у вас может быть несколько уровней отладки или подробностей, подобных этому.

# Set VERBOSE level as desired
# VERBOSE=0
VERBOSE=1
# VERBOSE=2

VERBOSE1='/dev/null'
VERBOSE2='/dev/null'

if [ "$VERBOSE" -gte 1 ]; then
  VERBOSE1='/dev/tty'
fi

if [ "$VERBOSE" -gte 2 ]; then
  VERBOSE2='/dev/tty'
fi

# actual script use commands like this
command > $VERBOSE1 2>&1

# or like this if you need
command 2> $VERBOSE2
person jperelli    schedule 13.10.2016