Как сохранить стандартную ошибку в переменной в скрипте Bash
допустим, у меня есть такой скрипт:
useless.sh
echo "This Is Error" 1>&2
echo "This Is Output"
и у меня есть другой скрипт:
alsoUseless.sh
./useless.sh | sed 's/Output/Useless/'
Я хочу захватить "это ошибка", или любой другой stderr от useless.sh, в переменную.
Назовем это ошибкой.
обратите внимание, что я использую stdout для чего-то. Я хочу продолжать использовать stdout, поэтому перенаправление stderr в stdout в этом случае не полезно.
так, в принципе, я хочу сделать
./useless.sh 2> $ERROR | ...
но это явно не работает.
Я также знаю, что я мог бы сделать
./useless.sh 2> /tmp/Error
ERROR=`cat /tmp/Error`
но это некрасиво и ненужно.
к сожалению, если здесь не будет ответов, это то, что мне придется сделать.
Я надеюсь, что есть другой путь.
у кого-нибудь есть лучше идеи?
14 ответов:
было бы аккуратнее захватить файл ошибки таким образом:
ERROR=$(</tmp/Error)оболочка распознает это и не должна работать'
cat' для получения данных.большой вопрос трудно. Я не думаю, что есть простой способ сделать это. Вам нужно будет построить весь конвейер в подшелле, в конечном итоге отправив его окончательный стандартный вывод в файл, чтобы вы могли перенаправить ошибки на стандартный вывод.
ERROR=$( { ./useless.sh | sed s/Output/Useless/ > outfile; } 2>&1 )обратите внимание, что требуется точка с запятой (в классические снаряды-Борн, Корн-наверняка; вероятно, в Баш тоже). В '
{}' выполняет перенаправление ввода-вывода по вложенным командам. Как написано, он будет захватывать ошибки изsedтоже.(формально непроверенный код-использование на свой страх и риск.)
alsoUseless.sh
это позволит вам передать вывод вашего
useless.shскрипт с помощью команды, такие какsedи сохранитьstderrв переменной с именемerror. Результат работы трубы отправляется наstdoutдля отображения или для передачи в другую команду.он устанавливает несколько дополнительных дескрипторов файлов для управления перенаправлениями, необходимыми для этого.
#!/bin/bash exec 3>&1 4>&2 #set up extra file descriptors error=$( { ./useless.sh | sed 's/Output/Useless/' 2>&4 1>&3; } 2>&1 ) echo "The message is \"${error}.\"" exec 3>&- 4>&- # release the extra file descriptors
Перенаправленный stderr в stdout, stdout в /dev / null, а затем используйте обратные палочки или
$()для захвата перенаправленного stderr:ERROR=$(./useless.sh 2>&1 >/dev/null)
есть много дубликатов для этого вопроса, многие из которых имеют немного более простой сценарий использования, где вы не хотите, чтобы захватить stderr и stdout и код выхода все в то же время.
if result=$(useless.sh 2>&1); then stdout=$result else rc=$? stderr=$result fiработает для общего сценария, где вы ожидаете либо правильный вывод в случае успеха, либо диагностическое сообщение на stderr в случае сбоя.
обратите внимание, что операторы управления оболочки уже проверяют
$?под капот, так что все, что выглядит какcmd if [ $? -eq 0 ], then ...это просто неуклюжий, однообразный способ сказать
if cmd; then ...
# command receives its input from stdin. # command sends its output to stdout. exec 3>&1 stderr="$(command </dev/stdin 2>&1 1>&3)" exitcode="${?}" echo "STDERR: $stderr" exit ${exitcode}
вот как я это сделал:
# # - name of the (global) variable where the contents of stderr will be stored # - command to be executed # captureStderr() { local tmpFile=$(mktemp) 2> $tmpFile eval "=$(< $tmpFile)" rm $tmpFile }пример использования :
captureStderr err "./useless.sh" echo -$err-Это тут использовать временный файл. Но, по крайней мере, уродливый материал завернут в функцию.
Это интересная проблема, к которой я надеялся, что было элегантное решение. К сожалению, я получаю решение, подобное г-ну Леффлеру, но я добавлю, что вы можете вызвать бесполезную изнутри функцию Bash для улучшения читаемости:
#!/bin/bash function useless { /tmp/useless.sh | sed 's/Output/Useless/' } ERROR=$(useless) echo $ERRORвсе другие виды перенаправления вывода должны быть поддержаны временным файлом.
этот пост помог мне придумать аналогичное решение для своих собственных целей:
MESSAGE=`{ echo $ERROR_MESSAGE | format_logs.py --level=ERROR; } 2>&1`затем, пока наше сообщение не является пустой строкой, мы передаем его другим вещам. Это позволит нам знать, если наши format_logs.py не удалось с каким-то исключением python.
захват и печать stderr
ERROR=$( ./useless.sh 3>&1 1>&2 2>&3 | tee /dev/fd/2 )распад
можно использовать
$()для захвата stdout, но вы хотите захватить stderr вместо этого. Итак, вы меняете stdout и stderr. Использование fd 3 в качестве временного хранилища в стандартном алгоритме подкачки.если вы хотите захватить и распечатать использовать
teeсделать дубликат. В этом случае выводteeбудет захвачен$()вместо того, чтобы перейти к консоли, но stderr (oftee) все равно будет идти на консоль, поэтому мы используем это в качестве второго выхода дляteeчерез специальный файл/dev/fd/2Сteeожидает путь к файлу, а не номер fd.Примечание: это очень много перенаправлений в одной строке и порядок имеет значение.
$()захватывает stdout изteeв конце трубопровода и самого трубопровода маршруты stdout из./useless.shк stdinteeпосле того, как мы поменяли stdin и stdout на./useless.sh.использование stdout of ./useless.sh
ОП сказал, что он все еще хотел использовать (а не просто печатать) stdout, например
./useless.sh | sed 's/Output/Useless/'.нет проблем просто сделать это перед заменой stdout и stderr. Я рекомендую переместить его в функцию или файл (also-useless.sh) и называя это вместо ./useless.sh в строке выше.
однако, если вы хотите захватить stdout и stderr, то я думаю, что вы должны вернуться к временным файлам потому что
$()будет делать только по одному за раз, и это делает подобласть, из которой вы не можете возвращать переменные.
если вы хотите обойти использование временного файла, вы можете использовать подстановку процесса. Я еще не совсем понял, как это работает. Это была моя первая попытка:
$ .useless.sh 2> >( ERROR=$(<) ) -bash: command substitution: line 42: syntax error near unexpected token `)' -bash: command substitution: line 42: `<)'тогда я попробовал
$ ./useless.sh 2> >( ERROR=$( cat <() ) ) This Is Output $ echo $ERROR # $ERROR is empty$ ./useless.sh 2> >( cat <() > asdf.txt ) This Is Output $ cat asdf.txt This Is Errorтак что процесс подстановки делает вообще правильно... к сожалению, всякий раз, когда я обернуть STDIN внутри
>( )С$()в попытке захватить это в переменную, я теряю содержание$(). Я думаю, что это потому, что$()запускает подпроцесс, который больше не имеет доступа к файловому дескриптору в /dev/fd, который принадлежит родительскому процессу.процесс замены купил мне возможность работать с потоком данных, который больше не находится в STDERR, к сожалению, я, кажется, не в состоянии манипулировать им так, как я хочу.
POSIX
STDERR можно захватить с помощью магии перенаправления:
$ { error=$( { { ls -ld /XXXX /bin | tr o Z ; } 1>&3 ; } 2>&1); } 3>&1 lrwxrwxrwx 1 rZZt rZZt 7 Aug 22 15:44 /bin -> usr/bin/ $ echo $error ls: cannot access '/XXXX': No such file or directoryобратите внимание, что трубопровод STDOUT команды (здесь
ls) делается внутри самого сокровенного{}. Если вы выполняете простую команду (например, не трубу), вы можете удалить эти внутренние скобки.вы не можете трубить вне команды, поскольку трубопровод делает подоболочку в
bashиzsh, и присвоение переменной в подобласти не будет доступно для текущая оболочка.Баш
на
bash, было бы лучше не предполагать, что файловый дескриптор 3 не используется:{ error=$( { { ls -ld /XXXX /bin | tr o Z ; } 1>&$tmp ; } 2>&1); } {tmp}>&1; exec {tmp}>&- # With this syntax the FD stays openобратите внимание, что это не работает в
zsh.
спасибо ответ для общей идеи.
на ошибок команды:
execute [INVOKING-FUNCTION] [COMMAND]
execute () { error=$( 2>&1 >/dev/null) if [ $? -ne 0 ]; then echo ": $error" exit 1 fi }
вдохновение в бережливом производстве:
Comments