Я запускаю скрипты оболочки из Jenkins, который запускает скрипты оболочки с параметрами shebang #!/bin/sh -ex
.
В соответствии сBash Shebang для чайников?, -x
, "заставляет оболочку выводить трассировку выполнения", что отлично подходит для большинства целей, за исключением эхо:
echo "Message"
производит вывод
+ echo "Message"
Message
что немного избыточно и выглядит немного странно. Есть ли способ оставить -x
включенным, но выводить только
Message
вместо двух строк выше, например, путем добавления к команде echo специального командного символа или перенаправления вывода?
решение1
Когда вы по горло в аллигаторах, легко забыть, что целью было осушить болото. — популярная поговорка
Вопрос в том,echo
, и все же большинство ответов до сих пор были сосредоточены на том, как незаметно set +x
ввести команду. Есть гораздо более простое и прямое решение:
{ echo "Message"; } 2> /dev/null
(Я признаю, что я бы, возможно, не подумал об этом, { …; } 2> /dev/null
если бы не увидел этого в предыдущих ответах.)
Это несколько громоздко, но если у вас есть блок последовательных echo
команд, вам не нужно делать это для каждой из них по отдельности:
{
echo "The quick brown fox"
echo "jumps over the lazy dog."
} 2> /dev/null
Обратите внимание, что при наличии переносов строк точка с запятой не нужна.
Вы можете уменьшить нагрузку при наборе текста, используяидея Кенорба
постоянно открывать /dev/null
нестандартный файловый дескриптор (например, 3), а затем все время говорить 2>&3
вместо .2> /dev/null
Первые четыре ответа на момент написания этой статьи требуют выполнения чего-то особенного (и, в большинстве случаев, обременительного)
каждыйвремя вы делаете echo
. Если вы действительно хотитевсе echo
команды для подавления трассировки выполнения (а почему бы и нет?), вы можете сделать это глобально, не портя много кода. Во-первых, я заметил, что псевдонимы не трассируются:
$ myfunc()
> {
> date
> }
$ alias myalias="date"
$ set -x
$ date
+ date
Mon, Oct 31, 2016 0:00:00 AM # Happy Halloween!
$ myfunc
+ myfunc # Note that function call is traced.
+ date
Mon, Oct 31, 2016 0:00:01 AM
$ myalias
+ date # Note that it doesn’t say + myalias
Mon, Oct 31, 2016 0:00:02 AM
(Обратите внимание, что следующие фрагменты скрипта работают, если шебанг — это #!/bin/sh
, даже если /bin/sh
это ссылка на bash. Но если шебанг — это #!/bin/bash
, вам нужно добавить shopt -s expand_aliases
команду, чтобы псевдонимы работали в скрипте.)
Итак, мой первый трюк:
alias echo='{ set +x; } 2> /dev/null; builtin echo'
Теперь, когда мы говорим echo "Message"
, мы вызываем псевдоним, который не трассируется. Псевдоним отключает опцию трассировки, одновременно подавляя сообщение трассировки от set
команды (используя технику, представленную сначала вответ пользователя 5071535), а затем выполняет фактическую echo
команду. Это позволяет нам получить эффект, аналогичный ответу пользователя 5071535, без необходимости редактировать код вкаждый echo
команда. Однако это оставляет режим трассировки выключенным. Мы не можем поместить a set -x
в псевдоним (или, по крайней мере, не так просто), потому что псевдоним позволяет только заменить слово строкой; никакая часть строки псевдонима не может быть введена в команду
послеаргументы (например, "Message"
). Так, например, если скрипт содержит
date
echo "The quick brown fox"
echo "jumps over the lazy dog."
date
выход будет
+ date
Mon, Oct 31, 2016 0:00:03 AM
The quick brown fox
jumps over the lazy dog.
Mon, Oct 31, 2016 0:00:04 AM # Note that it doesn’t say + date
поэтому вам все равно придется снова включать опцию трассировки после отображения сообщений — но только один раз после каждогоблокироватьпоследовательных echo
команд:
date
echo "The quick brown fox"
echo "jumps over the lazy dog."
set -x
date
Это было бы хорошоесли бы мы могли сделать set -x
автоматический после echo
— а мы можем, с небольшими уловками. Но прежде чем я это представлю, подумайте вот о чем. OP начинается со скриптов, которые используют shebang #!/bin/sh -ex
. Неявно пользователь может удалить x
из shebang и получить скрипт, который работает нормально, без трассировки выполнения. Было бы неплохо, если бы мы могли разработать решение, которое сохраняет это свойство. Первые несколько ответов здесь не соответствуют этому свойству, потому что они включают трассировку «обратно» после echo
операторов, безусловно, независимо от того, была ли она уже включена.
Этот ответявно не признает эту проблему, поскольку оназаменяет echo
вывод с выводом трассировки; поэтому все сообщения исчезают, если трассировка отключена. Сейчас я представлю решение, которое включает трассировку обратно после echo
оператораусловно— только если он уже был включен. Понижение этого до решения, которое включает трассировку «назад» безоговорочно, является тривиальным и остается в качестве упражнения.
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
echo_and_restore() {
builtin echo "$*"
case "$save_flags" in
(*x*) set -x
esac
}
$-
— это список опций; конкатенация букв, соответствующих всем установленным опциям. Например, если установлены опции e
и , то будет набор букв, включающий и . Мой новый псевдоним (выше) сохраняет значение до отключения трассировки. Затем, при отключенной трассировке, он передает управление функции оболочки. Эта функция выполняет фактическую работу, а затем проверяет, была ли опция включена при вызове псевдонима. Если опция была включена, функция снова включает ее; если она была выключена, функция оставляет ее выключенной.x
$-
e
x
$-
echo
x
Вы можете вставить указанные выше семь строк (восемь, если вы включите shopt
) в начало скрипта и оставить все остальное как есть.
Это позволит вам
- использовать любую из следующих линий шебанга:
#!/bin/sh -ex #!/bin/ш -е #!/bin/sh –x
или просто#!/bin/ш
и это должно работать так, как и ожидалось. - иметь такой код
(хижина) команда 1 команда 2 команда 3 набор -х команда 4 команда 5 команда 6 установить +х команда 7 команда 8 команда 9
и- Команды 4, 5 и 6 будут трассироваться — если только одна из них не является
echo
, в этом случае она будет выполнена, но не будет трассироваться. (Но даже если команда 5 являетсяecho
, команда 6 все равно будет трассироваться.) - Команды 7, 8 и 9 не будут отслеживаться. Даже если команда 8 — это
echo
, команда 9 все равно не будет отслеживаться. - Команды 1, 2 и 3 будут трассироваться (как 4, 5 и 6) или нет (как 7, 8 и 9) в зависимости от того, включает ли шебанг
x
.
- Команды 4, 5 и 6 будут трассироваться — если только одна из них не является
PS Я обнаружил, что в моей системе я могу опустить builtin
ключевое слово в моем среднем ответе (то, которое является просто псевдонимом для echo
). Это неудивительно; bash(1) говорит, что во время раскрытия псевдонима …
… слово, которое идентично псевдониму, который расширяется, не расширяется во второй раз. Это означает, что можно псевдоним
ls
кls -F
, например, и bash не пытается рекурсивно расширить заменяемый текст.
Неудивительно, что последний ответ (с echo_and_restore
) не срабатывает, если builtin
пропущено ключевое слово 1 . Но, как ни странно, он работает, если я удаляю builtin
и меняю порядок:
echo_and_restore() {
echo "$*"
case "$save_flags" in
(*x*) set -x
esac
}
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
__________
1 Кажется, это приводит к неопределенному поведению. Я видел
- бесконечный цикл (вероятно, из-за неограниченной рекурсии),
- сообщение об ошибке
/dev/null: Bad address
и - основной дамп.
решение2
Я нашел частичное решение здесьИнформИТ:
#!/bin/bash -ex
set +x;
echo "shell tracing is disabled here"; set -x;
echo "but is enabled here"
выходы
set +x;
shell tracing is disabled here
+ echo "but is enabled here"
but is enabled here
К сожалению, это все еще слышно set +x
, но, по крайней мере, после этого наступает тишина. Так что это, по крайней мере, частичное решение проблемы.
Но, может быть, есть способ сделать это лучше? :)
решение3
Этот способ улучшает ваше собственное решение, избавляясь от вывода set +x
:
#!/bin/bash -ex
{ set +x; } 2>/dev/null
echo "shell tracing is disabled here"; set -x;
echo "but is enabled here"
решение4
Поместите это set +x
в скобки, чтобы это применялось только в локальной сфере.
Например:
#!/bin/bash -x
exec 3<> /dev/null
(echo foo1 $(set +x)) 2>&3
($(set +x) echo foo2) 2>&3
( set +x; echo foo3 ) 2>&3
true
выведет:
$ ./foo.sh
+ exec
foo1
foo2
foo3
+ true