Bash - интерпретация содержимого переменной - PullRequest
2 голосов
/ 08 июня 2011

Как заставить Bash интерпретировать содержимое переменной как перенаправления ввода / вывода, а не просто передавать это содержимое выполняемой команде.Возьмем, к примеру, этот скрипт:

#!/bin/bash
test "$1" == '--log' && LOGGING="2>&1 | tee time.log" || LOGGING=""
date $LOGGING

Желаемое поведение - когда я запускаю этот скрипт с опцией --log, bash выполнит

$ date 2> & 1 |tee time.log

Если я не укажу --log, он просто выводит дату без создания журнала.Вместо этого он передает содержимое $ LOGGING на сегодняшний день в качестве аргумента CLI, что приводит к ошибке:

date: extra operand `|' Try `date
 --help' for more information.

Есть ли способ сделать это без написания чего-то вроде

#!/bin/bash
test "$1" == '--log' && date 2>&1 | tee time.log || date

Фактическое приложениеочевидно, это намного сложнее, чем просто вызвать «date», поэтому я бы хотел избежать копирования и вставки этой команды дважды в if if, просто чтобы добавить команды перенаправления и регистрации.

Ответы [ 3 ]

2 голосов
/ 09 июня 2011

Если ваш скрипт довольно длинный и вы хотите записывать все stdout и stderr при передаче --log, я предлагаю использовать exec для перенаправления всего. Смотрите эту отличную статью:

http://www.linuxjournal.com/content/bash-redirections-using-exec

#!/bin/bash
if [[ "$1" == '--log' ]]; then
    npipe=/tmp/$$.tmp
    trap "rm -f $npipe" EXIT
    mknod $npipe p
    tee <$npipe log &
    exec 1>&-
    exec 1>$npipe
fi

date
# and any other commands after this will be logged too.

Интересным в этом подходе является то, что вы также можете добавлять все зарегистрированные строки к временной метке, используя perl или gawk или другую утилиту:

#!/bin/bash
if [[ "$1" == '--log' ]]; then
    npipe=/tmp/$$.tmp
    trap "rm -f $npipe" EXIT
    mknod $npipe p
    perl -pne 'print scalar(localtime()), " ";' < $npipe | tee time.log &
    exec 1>&-
    exec 1>$npipe 2>&1
fi

echo hello world
echo hello world 2

После запуска time.log будет содержать:

$ cat time.log 
Wed Jun  8 13:28:45 2011 hello world
Wed Jun  8 13:28:45 2011 hello world 2

Недостатком здесь является то, что отметка времени печатается и на вашем терминале.

1 голос
/ 09 июня 2011

Проблема заключается в том, что, помещая команду в переменную , вы фактически конвертируете все в строки, а не оставляете ее в качестве ключевых слов Bash.Попробуйте добавить -x к строке shebang:

$ ./test.sh --log
+ test --log == --log
+ LOGGING='2>&1 | tee time.log'
+ date '2>&1' '|' tee time.log
date: extra operand `|'
Try `date --help' for more information.

Попробуйте это:

#!/bin/bash -x
logging() {
    if [ "$1" == '--log' ]
    then
        cat 2>&1 | tee time.log
    else
        cat
    fi
}
date | logging "$1"
1 голос
/ 08 июня 2011

Вы можете использовать eval:

eval date $LOGGING
...