Когда вы по уши в аллигаторах, легко забыть, что целью было осушить болото.
- народная поговорка
Вопрос в том 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
Обратите внимание, что вам не нужны точки с запятой, когда у вас есть новые строки.
Вы можете уменьшить нагрузку на типирование, используя идею kenorb -/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
(Обратите внимание, что следующие фрагменты сценария работают, если shebang есть #!/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
команды. Однако это оставляет режим трассировки выключенным. Мы не можем вставить 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
- и мы можем, с немного более хитрым. Но прежде чем представить это, подумайте об этом. ОП начинается со сценариев, которые используют #!/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 / sh -e
#! / bin / sh –x
или просто#! / Bin / ш
и это должно работать как ожидалось.
- иметь такой код
(Шебанг)
команда 1
команда 2
команда 3
установить -x
команда 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
, например, и Баш не пытаться рекурсивно расширить текст замены.
Не удивительно, что последний ответ (тот, что с 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
сообщение об ошибке, и
- основной дамп.
echo +x; echo "$*"; echo -x
в псевдониме, я хотел бы увидеть это.Я нашел частичное решение в InformIT :
выходы
К сожалению, это все еще отголоски
set +x
, но по крайней мере после этого тихо. так что это как минимум частичное решение проблемы.Но есть ли лучший способ сделать это? :)
источник
Этот способ улучшает ваше собственное решение, избавляясь от
set +x
вывода:источник
Поместите
set +x
в скобки, так что это будет применяться только для локальной области.Например:
будет выводить:
источник
set +x
внутренняя часть подоболочки (или вообще не использующая подоболочки) делает что-то полезное. Вы можете удалить его и получить тот же результат. Это перенаправление stderr в / dev / null, которое выполняет работу по временному «отключению» трассировки ... Кажетсяecho foo1 2>/dev/null
, и т. Д. Было бы так же эффективно и более читабельно.bash -x
), и вы уже перенаправляете&2
на ноль (так как&3
был перенаправлен на ноль), поэтому я не уверен, насколько этот комментарий уместен. Может быть, нам просто нужен лучший пример, который иллюстрирует вашу точку зрения, но, по крайней мере, в данном примере все еще кажется, что его можно упростить, не теряя никакой выгоды.Мне нравится исчерпывающий и хорошо объясненный ответ от g-man , и я считаю его лучшим из представленных на данный момент. Он заботится о контексте скрипта и не вызывает конфигурации, когда они не нужны. Итак, если вы читаете этот ответ, сначала попробуйте и проверьте его, вся заслуга в этом.
Однако в этом ответе пропущена важная часть: предложенный метод не будет работать для типичного варианта использования, то есть сообщения об ошибках:
Из-за того, как построен псевдоним, это расширится до
и как вы уже догадались,
echo_and_restore
исполняется всегда , безоговорочно. Учитывая, чтоset +x
часть не выполнялась, это означает, что содержимое этой функции также будет напечатано.Изменение последнего ,
;
чтобы&&
не сработало, потому что в Bash,||
и&&
являются левоассоциативными .Я нашел модификацию, которая работает для этого варианта использования:
Он использует подоболочку (
(...)
часть), чтобы сгруппировать все команды, а затем передает входную строку через stdin как Here String (<<<
вещь), которая затем печатаетсяcat -
. Опция-
необязательна, но вы знаете, « явное лучше, чем неявное ».Вы также можете использовать
cat -
напрямую без echo , но мне нравится этот способ, потому что он позволяет мне добавлять любые другие строки в вывод. Например, я склонен использовать это так:И теперь это работает прекрасно:
источник
Трассировка выполнения идет к
stderr
, фильтруйте это следующим образом:Некоторое объяснение, шаг за шагом:
stderr
перенаправлен ... -2>
>(…)
grep
это команда ...^
+ echo
...grep
переворачивает матч ... --v
stdout
; мы перенаправляем его туда,stderr
где он принадлежит. ->&2
Проблема в том (я думаю), что это решение может десинхронизировать потоки. Из-за фильтрации
stderr
может быть немного поздно по отношению кstdout
(гдеecho
вывод принадлежит по умолчанию). Чтобы это исправить, вы можете сначала присоединиться к потокам, если не возражаете против их обоихstdout
:Вы можете встроить такую фильтрацию в сам скрипт, но этот подход наверняка склонен к десинхронизации (то есть это происходило в моих тестах: след выполнения команды может появиться после вывода сразу следующего
echo
).Код выглядит так:
Запустите его без каких-либо хитростей:
Опять же, используйте
> >(grep -v "^+ echo ") 2>&1
для поддержания синхронизации за счет объединения потоков.Другой подход. Вы получаете «немного избыточный» и странно выглядящий вывод, потому что ваш терминал смешивает
stdout
иstderr
. Эти два потока являются разными животными по причине. Проверьте, соответствует ли анализstderr
только вашим потребностям; отменитьstdout
:Если в вашем скрипте есть
echo
сообщение об отладке / ошибке печати,stderr
вы можете избавиться от избыточности способом, описанным выше. Полная команда:На этот раз мы работаем
stderr
только с десинхронизацией, поэтому больше не проблема. К сожалению, таким образом, вы не увидите ни следа, ни выводаecho
этих отпечатковstdout
(если есть). Мы могли бы попытаться перестроить наш фильтр, чтобы обнаружить перенаправление (>&2
), но если вы посмотритеecho foobar >&2
,echo >&2 foobar
иecho "foobar >&2"
тогда вы, вероятно, согласитесь, что все усложняется.Многое зависит от того, что есть в ваших сценариях. Подумайте дважды, прежде чем применять какой-либо сложный фильтр, это может иметь неприятные последствия. Лучше иметь немного избыточности, чем случайно пропустить важную информацию.
Вместо того, чтобы отбрасывать след выполнения,
echo
мы можем отбросить его вывод - и любой вывод, кроме следов. Чтобы анализировать только следы выполнения, попробуйте:Защищенное? Подумай, что будет, если
echo "+ rm -rf --no-preserve-root /" >&2
в сценарии есть. У кого-то может случиться сердечный приступ.И наконец…
К счастью, есть
BASH_XTRACEFD
переменная окружения. Отman bash
:Мы можем использовать это так:
Обратите внимание, что первая строка порождает подоболочку. Таким образом, дескриптор файла не останется действительным, как и переменная, назначенная в текущей оболочке впоследствии.
Благодаря
BASH_XTRACEFD
вам вы можете анализировать следы без эха и любых других выходных данных, какими бы они ни были. Это не совсем то, что вы хотели, но мой анализ заставляет меня думать, что это (в общем) правильный путь.Конечно, вы можете использовать другой метод, особенно когда вам нужно проанализировать
stdout
и / илиstderr
вместе с вашими следами. Вам просто нужно помнить, что есть определенные ограничения и подводные камни. Я пытался показать (некоторые из них) их.источник
В Makefile вы можете использовать
@
символ.Пример использования:
@echo 'message'
Из этого документа GNU:
источник
./test.sh: line 1: @echo: command not found
, но я использую Bash.@
работает только когда вы повторяете в make-файлах.Отредактировано 29 октября 2016 г. для модератора, предложения о том, что оригинал не содержал достаточно информации, чтобы понять, что происходит.
Этот «трюк» производит только одну строку вывода сообщения на терминале, когда активен xtrace:
Первоначальный вопрос: есть ли способ оставить -x включенным, но выводить только сообщение вместо двух строк ? Это точная цитата из вопроса.
Я понимаю вопрос, как "оставить set -x включенным И создать одну строку для сообщения"?
Итак, в итоге, ОП требует:
OP не требует использования команды echo . Они привели это в качестве примера создания сообщения, используя сокращение, например, которое означает латинский examplelitia или «например».
Думая «нестандартно» и отказываясь от использования echo для создания сообщений, я отмечаю, что оператор присваивания может удовлетворить все требования.
Сделайте присвоение текстовой строки (содержащей сообщение) неактивной переменной.
Добавление этой строки в скрипт не изменяет никакой логики, но выдает одну строку, когда трассировка активна: (Если вы используете переменную $ echo , просто измените имя на другую неиспользуемую переменную)
На терминале вы увидите только 1 строку:
не два, как не любит ОП:
Вот пример сценария для демонстрации. Обратите внимание, что подстановка переменных в сообщение (имя каталога $ HOME через 4 строки в конце) работает, поэтому вы можете отслеживать переменные, используя этот метод.
И вот результат его запуска в корне, а затем в домашнем каталоге.
источник
echo "Here are the files in this directory:" *
?