Я хочу , чтобы выполнить давно работающую команду в Bash, и оба захват его статуса выхода, и тройник его выхода.
Итак, я делаю это:
command | tee out.txt
ST=$?
Проблема в том, что переменная ST фиксирует состояние выхода команды, tee
а не команды. Как я могу решить это?
Обратите внимание, что команда выполняется долго и перенаправление вывода в файл для последующего просмотра не является хорошим решением для меня.
bash
shell
error-handling
pipe
ЭЛЕКТРОДИСТАНЦИОННАЯ СИСТЕМА УПРАВЛЕНИЯ
источник
источник
Ответы:
Существует внутренняя переменная Bash, которая называется
$PIPESTATUS
; это массив, в котором хранится состояние выхода каждой команды в последнем конвейере команд переднего плана.Или другой вариант, который также работает с другими оболочками (например, zsh), - включить pipefail:
Первый вариант не работает
zsh
из-за немного другого синтаксиса.источник
exit ${PIPESTATUS[0]}
.set -o pipefail
полезно использовать bash'sисточник
( set -o pipefail; command | tee out.txt ); ST=$?
set -o pipefail
и затем выполнить команду, а затем сразу же выполнить команду,set +o pipefail
чтобы отменить выбор.-o pipefail
его помощью он узнает, что канал не работает, но если оба «command» и «tee» потерпят неудачу, он получит код выхода от «tee».Тупое решение: соединить их через именованную трубу (mkfifo). Тогда команда может быть запущена второй.
источник
mkfifo
и могут вместо этого потребовать,mknod -p
если я правильно помню.mkfifo
илиmknod -p
: в моем случае правильная команда для создания файла канала былаmknod FILE_NAME p
.Есть массив, который дает вам статус выхода каждой команды в канале.
источник
Это решение работает без использования специальных функций bash или временных файлов. Бонус: в конце концов, статус выхода - это статус выхода, а не какая-то строка в файле.
Ситуация:
Вы хотите статус выхода из
someprog
и выход изfilter
.Вот мое решение:
Смотрите мой ответ на тот же вопрос на unix.stackexchange.com для подробного объяснения и альтернативы без подоболочек и некоторых предостережений.
источник
Комбинируя
PIPESTATUS[0]
и результат выполненияexit
команды в подоболочке, вы можете напрямую получить доступ к возвращаемому значению вашей начальной команды:command | tee ; ( exit ${PIPESTATUS[0]} )
Вот пример:
даст тебе:
return value: 1
источник
VALUE=$(might_fail | piping)
которая не устанавливает PIPESTATUS в мастер-оболочке, но устанавливает уровень ошибки. Используя:VALUE=$(might_fail | piping; exit ${PIPESTATUS[0]})
я получаю хочу я хотел.command_might_fail | grep -v "line_pattern_to_exclude" || exit ${PIPESTATUS[0]}
в случае не ти, а grep фильтрацииПоэтому я хотел дать ответ, подобный ответу Лесманы, но я думаю, что мой, пожалуй, немного проще и немного более выгодно решение с чистой оболочкой Борна:
Я думаю, это лучше всего объяснить изнутри - command1 выполнит и напечатает свой обычный вывод на stdout (дескриптор файла 1), затем, как только это будет сделано, printf выполнит и напечатает код выхода icommand1 на своем stdout, но этот stdout перенаправляется на дескриптор файла 3.
Пока команда command1 выполняется, ее стандартный вывод передается в command2 (вывод printf никогда не попадает в command2, потому что мы отправляем его в файловый дескриптор 3 вместо 1, который читает канал). Затем мы перенаправляем вывод command2 в файловый дескриптор 4, чтобы он также не входил в файловый дескриптор 1 - потому что мы хотим, чтобы файловый дескриптор 1 был немного позже, потому что мы приведем вывод printf для файлового дескриптора 3 обратно в файловый дескриптор 1 - потому что это то, что команда замещения (обратные метки), будет захватывать, и это то, что будет помещено в переменную.
Последнее волшебство в том, что сначала
exec 4>&1
мы делали это отдельной командой - она открывает дескриптор файла 4 как копию стандартного вывода внешней оболочки. Подстановка команд будет захватывать все, что написано в стандарте, с точки зрения команд внутри него - но поскольку выходные данные команды 2 собираются в файловом дескрипторе 4, если речь идет о подстановке команд, подстановка команд не захватывает это - однако, как только это произойдет "выходит" из подстановки команды, она фактически все еще идет к общему дескриптору файла скрипта 1.(Это
exec 4>&1
должна быть отдельная команда, потому что многим распространенным оболочкам не нравится, когда вы пытаетесь записать в файловый дескриптор внутри подстановки команды, которая открывается во «внешней» команде, использующей подстановку. Так что это Самый простой портативный способ сделать это.)Вы можете посмотреть на это менее технически и более игриво, как если бы результаты команд перепрыгивали друг друга: команда 1 отправляет канал команде 2, затем вывод printf перепрыгивает через команду 2, так что команда 2 не перехватывает ее, а затем выходные данные команды 2 перепрыгивают из подстановки команд так же, как и printf попадает как раз вовремя, чтобы быть захваченным подстановкой, так что она попадает в переменную, а выходные данные команды 2 продолжают веселиться, записываясь в стандартный вывод, так же, как в обычной трубе.
Кроме того, насколько я понимаю, он по-
$?
прежнему будет содержать код возврата второй команды в конвейере, поскольку назначения переменных, подстановки команд и составные команды эффективно прозрачны для кода возврата команды внутри них, поэтому статус возврата command2 должен быть распространен - это, и не нужно определять дополнительную функцию, поэтому я думаю, что это могло бы быть несколько лучшим решением, чем предложенное lesmana.В соответствии с предостережениями, которые упоминает лесмена, вполне возможно, что в какой-то момент команда1 будет использовать файловые дескрипторы 3 или 4, поэтому для большей надежности вы должны сделать следующее:
Обратите внимание, что в моем примере я использую составные команды, но подоболочки (использование
( )
вместо{ }
будет также работать, хотя, возможно, будет менее эффективным.)Команды наследуют файловые дескрипторы от процесса, который их запускает, поэтому вся вторая строка наследует файловый дескриптор четыре, а составная команда, за которой следует,
3>&1
наследует файловый дескриптор три. Таким образом, команда4>&-
гарантирует, что внутренняя составная команда не унаследует файловый дескриптор 4, и3>&-
не унаследует файловый дескриптор 3, поэтому команда 1 получает «более чистую», более стандартную среду. Вы также можете перемещать внутреннее4>&-
рядом с3>&-
, но я понимаю, почему бы просто не ограничить его область настолько, насколько это возможно.Я не уверен, как часто вещи используют файловый дескриптор 3 и 4 напрямую - я думаю, что большинство программ используют системные вызовы, которые возвращают неиспользуемые в данный момент файловые дескрипторы, но иногда код записывает код непосредственно в файловый дескриптор 3, я предположить (я мог бы представить программу, проверяющую дескриптор файла, чтобы увидеть, открыт ли он, и использующую его, если он есть, или соответствующим образом ведущий себя иначе, если это не так). Поэтому последнее, вероятно, лучше всего учитывать и использовать в случаях общего назначения.
источник
В Ubuntu и Debian вы можете
apt-get install moreutils
. Он содержит утилиту,mispipe
которая возвращает состояние выхода первой команды в канале.источник
В отличие от ответа @ cODAR, он возвращает исходный код завершения первой команды, а не только 0 для успеха и 127 для ошибки. Но, как заметил @Chaoran, вы можете просто позвонить
${PIPESTATUS[0]}
. Однако важно, чтобы все было заключено в квадратные скобки.источник
Вне Bash вы можете сделать:
Это полезно, например, в сценариях ниндзя, где ожидается использование оболочки
/bin/sh
.источник
PIPESTATUS [@] должен быть скопирован в массив сразу после возврата команды pipe. Любое чтение PIPESTATUS [@] удалит содержимое. Скопируйте его в другой массив, если вы планируете проверить состояние всех команд конвейера. "$?" это то же значение, что и последний элемент «$ {PIPESTATUS [@]}», и чтение его, похоже, уничтожает «$ {PIPESTATUS [@]}», но я не совсем это подтвердил.
Это не будет работать, если труба находится в под-оболочке. Для решения этой проблемы
смотрите bash pipestatus в команде backticked?
источник
Самый простой способ сделать это в обычном bash - это использовать замену процесса вместо конвейера. Есть несколько отличий, но они, вероятно, не имеют большого значения для вашего варианта использования:
pipefail
Параметр иPIPESTATUS
переменная не имеют никакого отношения к замене процесса.С заменой процесса, bash просто запускает процесс и забывает об этом, он даже не виден в
jobs
.Упомянутые различия в стороне
consumer < <(producer)
иproducer | consumer
по сути эквивалентны.Если вы хотите переключить, какой из них является «основным» процессом, вы просто переключаете команды и направление замены на
producer > >(consumer)
. В твоем случае:Пример:
Как я уже сказал, есть отличия от выражения канала. Процесс может никогда не остановиться, если он не чувствителен к закрытию трубы. В частности, он может продолжать записывать вещи в ваш стандартный вывод, что может сбивать с толку.
источник
Чистый раствор оболочки:
А теперь со второго
cat
заменилиfalse
:Пожалуйста, обратите внимание, что первая кошка тоже терпит неудачу, потому что на нее закрывается стандартный вывод. Порядок неудачных команд в журнале в этом примере правильный, но не полагайтесь на него.
Этот метод позволяет захватывать stdout и stderr для отдельных команд, так что вы можете затем скопировать их в файл журнала, если возникает ошибка, или просто удалить ее, если ошибки нет (например, вывод dd).
источник
Основываясь на ответе @ brian-s-wilson; эта вспомогательная функция bash:
используется таким образом:
1: get_bad_things должен быть успешным, но он не должен производить никаких выходных данных; но мы хотим увидеть результат, который он производит
2: весь конвейер должен быть успешным
источник
Иногда может быть проще и понятнее использовать внешнюю команду, чем копаться в деталях bash. pipe , из минимального языка сценариев процесса execline , завершается с кодом возврата второй команды *, так же как и
sh
конвейер, но в отличие от негоsh
, он позволяет изменить направление канала, чтобы мы могли захватить код возврата производителя процесс (ниже все это вsh
командной строке, но сexecline
установленным):Использование
pipeline
имеет те же отличия от собственных конвейеров bash, что и замена процесса bash, использованная в ответе # 43972501 .* На самом деле
pipeline
не выходит вообще, если нет ошибки. Он выполняется во второй команде, поэтому это вторая команда, которая выполняет возврат.источник