Подавить трассировку выполнения bash (set -x) снаружи скрипта

17

Я пытался найти ответ на этот вопрос, но пока не получилось:

У меня есть сценарий, который запускает некоторые другие сценарии, и многие из этих других сценариев содержат «set -x», что заставляет их печатать каждую команду, которую они выполняют. Я хотел бы избавиться от этого, но сохранить информацию, если какой-либо из сценариев отправит сообщение об ошибке в stderr.

Так что я не могу просто написать ./script 2>/dev/null

Кроме того, у меня нет прав для редактирования этих других скриптов, поэтому я не могу вручную изменить опцию set.

Я думал о регистрации всего от stderr до отдельного файла и фильтрации команд трассировки, но, может быть, есть более простой способ?

человек
источник
1
./script 2>some_file
Satō

Ответы:

25

С bash4.1 и выше, вы можете сделать

BASH_XTRACEFD=7 ./script.bash 7> /dev/null

(также работает, когда bashвызывается как sh).

По сути, мы говорим bashвывести xtraceвыходные данные по дескриптору файла 7 вместо значения по умолчанию 2 и перенаправить этот дескриптор файла в /dev/null. Число FD является произвольным. Используйте fd выше 2, который иначе не используется в вашем скрипте. Если оболочка, в которую вы вводите эту команду, - это bashили yash, вы можете даже использовать число выше 9 (хотя вы можете столкнуться с проблемами, если дескриптор файла используется внутри оболочки).

Если оболочка, из которой вы вызываете этот bashскрипт zsh, вы также можете сделать:

(export BASH_XTRACEFD; ./script.bash {BASH_XTRACEFD}> /dev/null)

для переменной, которой автоматически присваивается первый свободный fd выше 9.

Для более старых версий bash, другой вариант, если xtraceон включен с set -x(в отличие от #! /bin/bash -xили set -o xtrace), будет переопределять setкак экспортированную функцию, которая ничего не делает при передаче -x(хотя это сломало бы скрипт, если он (или любой другой bashскрипт, который он вызывает) используется setдля установки позиционных параметров).

Подобно:

set()
  case $1 in
    (-x) return 0;;
    (-[!-]|"") builtin set "$@";;
    (*) echo >&2 That was a bad idea, try something else; builtin set "$@";;
  esac

export -f set
./script.bash

Другой вариант - добавить ловушку DEBUG в $BASH_ENVфайл, который выполняется set +xперед каждой командой.

echo 'trap "{ set +x; } 2>/dev/null" DEBUG' > ~/.no-xtrace
BASH_ENV=~/.no-xtrace ./script.bash

Это не будет работать, когда set -xэто делается в под-оболочке.

Как сказал @ilkkachu, если у вас есть разрешение на запись в любую папку в файловой системе, вы, по крайней мере, сможете сделать копию сценария и отредактировать его.

Если вы нигде не можете написать копию сценария или если вам неудобно создавать и редактировать новую копию каждый раз, когда происходит обновление исходного сценария, вы все равно можете сделать следующее:

 bash <(sed 's/set -x/set +x/g' ./script.bash)

Это (и подход к копированию) может не работать должным образом, если сценарий выполняет какие- либо действия с $0такими специальными переменными, как $BASH_SOURCE(например, поиск файлов, относящихся к местоположению самого сценария), поэтому вам может потребоваться выполнить еще какое-то редактирование, например заменить $0на путь сценария ...

Стефан Шазелас
источник
Ваш первый ответ именно то, что мне нужно, чистый и элегантный. Не могли бы вы немного объяснить, как это работает? Почему номер 7? Для чего могут быть использованы другие числа? Благодарю.
человек
@human, смотрите правку
Стефан
1
{BASH_XTRACEFD}>Трюк работает в bash4.1 или более поздней версии , а также.
Чепнер
@chepner, да, функция была добавлена ​​в zsh, ksh93 и bash одновременно по предложению разработчика zsh. Но здесь это не работает для ksh93или bashв том, что переменная не передается в среде команды (сравните <shell> -c 'export fd; printenv fd {fd}> /dev/null'в zsh, bashи ksh93). Вы можете сделать так, чтобы он работал в ksh93/ bash, выполняя это за два шага или, возможно, используя eval, но для bashэтого это может иметь побочные эффекты, если включена опция xtrace.
Стефан
5

Поскольку они являются сценариями, вы можете делать их копии и редактировать их.

Помимо этого, фильтрация выходных данных может показаться простой, вы можете явно установить PS4что-то более необычное, чем один плюс, чтобы упростить фильтрацию:

PS4="%%%%" bash script.sh 2>&1 | grep -ve '^%%%%'

(конечно, это рухнет stdout и stdin, но пайпинг просто stderr в Bash становится немного проблематичным, поэтому я проигнорирую это)

ilkkachu
источник
1
Трубить просто стдерр легко PS4="%%%%" bash script.sh 2> >(grep -ve '^%%%%').
Патрик
4
@Patrick, при выполнении этого в in возникают bashпроблемы, так как grepон запускается асинхронно (bash его не ждет, поэтому он может (и часто это делает) выводить данные после запуска следующей команды в сценарии).
Стефан