Я работаю над сценарием оболочки, который создает сложную команду из переменных, например, вот так (с техникой, которую я изучил из Bash FAQ ):
#!/bin/bash
SOME_ARG="abc"
ANOTHER_ARG="def"
some_complex_command \
${SOME_ARG:+--do-something "$SOME_ARG"} \
${ANOTHER_ARG:+--with "$ANOTHER_ARG"}
Этот скрипт динамически добавляет параметры --do-something "$SOME_ARG"
и --with "$ANOTHER_ARG"
в some_complex_command
случае определены эти переменные. Пока это работает нормально.
Но теперь я также хочу иметь возможность печатать или регистрировать команду, когда я ее запускаю, например, когда мой скрипт выполняется в режиме отладки. Поэтому, когда мой скрипт выполняется some_complex_command --do-something abc --with def
, я также хочу, чтобы эта команда была внутри переменной, чтобы я мог, например, записать ее в системный журнал.
Bash FAQ демонстрирует методику использования DEBUG
ловушки и $BASH_COMMAND
переменной (например, в целях отладки) для этой цели. Я пробовал это с помощью следующего кода:
#!/bin/bash
ARG="test string"
trap 'COMMAND="$BASH_COMMAND"; trap - DEBUG' DEBUG
echo "$ARG"
echo "Command was: ${COMMAND}"
Это работает, но не раскрывает переменные в команде:
host ~ # ./test.sh
test string
Command was: echo "$ARG"
Я предполагаю , что я должен использовать Eval для расширения , echo "$ARG"
чтобы echo test string
(по крайней мере , я не нашел способ , не все eval
же). Следующее работает:
eval echo "Command was: ${COMMAND}"
Он производит следующий вывод:
host ~ # ./test.sh
test string
Command was: echo "$ARG"
Command was: echo test string
Но я не совсем уверен, смогу ли я использовать eval
безопасно, как это. Я безуспешно пытался использовать некоторые вещи:
#!/bin/bash
ARG="test string; touch /x"
DANGER='$(touch /y; cat /etc/shadow)'
trap 'COMMAND="$BASH_COMMAND"; trap - DEBUG' DEBUG
echo "$ARG" $DANGER
echo "Command was: ${COMMAND}"
eval echo "Command was: ${COMMAND}"
Кажется, с этим хорошо справляются, но мне любопытно, если кто-то еще увидит проблему, которую я пропустил.
Ответы:
Одной из возможностей является создание функции-оболочки, которая будет одновременно печатать команду и выполнять ее следующим образом:
Так что в вашем скрипте вы можете сделать:
Не нужно возиться с ловушкой. Недостатком является то, что он добавляет несколько
debug
ключевых слов по всему вашему коду (но это должно быть хорошо, такие вещи обычно встречаются, как утверждения и т. Д.).Вы даже можете добавить глобальную переменную
DEBUG
и изменитьdebug
функцию следующим образом:Тогда вы можете назвать свой скрипт как:
или
или просто
в зависимости от того, хотите ли вы иметь отладочную информацию или нет.
Я прописал
DEBUG
переменную в заглавную, потому что она должна рассматриваться как переменная окружения.DEBUG
это тривиальное и распространенное имя, поэтому оно может конфликтовать с другими командами. Может быть , назвать этоGNIOURF_DEBUG
илиMARTIN_VON_WITTICH_DEBUG
илиUNICORN_DEBUG
если вы любите единорогов (и тогда вы , вероятно , как пони тоже).Заметка. В
debug
функции я тщательно отформатировал каждый аргументprintf '%q'
так, чтобы вывод был правильно экранирован и заключен в кавычки, чтобы быть дословно многократно используемым с прямым копированием и вставкой. Он также покажет вам, что именно увидела оболочка, поскольку вы сможете выяснить каждый аргумент (в случае пробелов или других забавных символов). Эта функция также использует прямое назначение с-v
переключателем,printf
чтобы избежать ненужных подоболочек.источник
&
. Мне пришлось перенести их в функцию, чтобы заставить ее работать - не очень приятно, но я не думаю, что есть лучший способ. Но не более тогоeval
, так что у меня все получилось, что приятно :)eval "$BASH_COMMAND"
запускает командуprintf '%s\n' "$BASH_COMMAND"
печатает указанную команду, а также перевод строки.Если команда содержит переменные (т. Е. Если это что-то подобное
cat "$foo"
), то при распечатке команды печатается текст переменной. Невозможно напечатать значение переменной без выполнения команды - думайте о командах какvariable=$(some_function) other_variable=$variable
.Самый простой способ получить трассировку от выполнения сценария оболочки - установить параметр
xtrace
оболочки, запустив сценарий какbash -x /path/to/script
или вызвав егоset -x
внутри оболочки. Трассировка печатается со стандартной ошибкой.источник
xtrace
, но это не дает мне много контроля. Я попытался "set -x; ...; set + x", но: 1) команда "set + x" для отключения xtrace тоже напечатана 2) Я не могу поставить префикс вывода, например, с отметкой времени 3) Я могу войти в системный журнал.BASH_COMMAND
которой содержится расширенная команда, потому что в какой-то момент он должен в любом случае выполнять расширение переменной для команды при ее выполнении :)