Подавить трассировку выполнения команды echo?

Подавить трассировку выполнения команды echo?

Я запускаю скрипты оболочки из 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$-ex$-echox

Вы можете вставить указанные выше семь строк (восемь, если вы включите shopt) в начало скрипта и оставить все остальное как есть.

Это позволит вам

  1. использовать любую из следующих линий шебанга:
    #!/bin/sh -ex
    #!/bin/ш -е
    #!/bin/sh –x
    или просто
    #!/bin/ш
    и это должно работать так, как и ожидалось.
  2. иметь такой код
    (хижина)
    команда 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.

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

Связанный контент