Допустим, у меня есть скрипт, подобный следующему:
useless.sh
echo "This Is Error" 1>&2
echo "This Is Output"
И у меня есть другой сценарий оболочки:
alsoUseless.sh
./useless.sh | sed 's/Output/Useless/'
Я хочу записать «This Is Error» или любой другой stderr из useless.sh в переменную. Давайте назовем это ОШИБКА.
Обратите внимание, что я использую стандартный вывод для чего-то. Я хочу продолжать использовать stdout, поэтому перенаправление stderr в stdout в этом случае бесполезно.
Итак, в основном, я хочу сделать
./useless.sh 2> $ERROR | ...
но это, очевидно, не работает.
Я также знаю, что я мог сделать
./useless.sh 2> /tmp/Error
ERROR=`cat /tmp/Error`
но это безобразно и ненужно.
К сожалению, если здесь не будет ответов, это то, что я собираюсь сделать.
Я надеюсь, что есть другой способ.
У кого-нибудь есть идеи получше?
ERROR=$(./useless.sh | sed 's/Output/Useless/' 2>&1 1>/dev/ttyX)
Ответы:
Было бы лучше записать файл ошибок так:
Оболочка распознает это и не должна запускать '
cat
' для получения данных.Большой вопрос сложен. Я не думаю, что есть простой способ сделать это. Вам придется встроить весь конвейер в под-оболочку, в конечном итоге отправив окончательный стандартный вывод в файл, чтобы вы могли перенаправить ошибки в стандартный вывод.
Обратите внимание, что точка с запятой необходима (в классических оболочках - Bourne, Korn - наверняка; вероятно, в Bash тоже). '
{}
' Выполняет перенаправление ввода / вывода поверх вложенных команд. Как написано, это будет захватывать ошибки отsed
слишком.источник
/dev/null
вместоoutfile
(Если вы похожи на меня, вы нашли этот вопрос через Google, и у вас нет тех же требований, что и у ОП)stdout
иstderr
вперед и назад. Но будьте осторожны , как здесь сказано: в bash было бы лучше не предполагать, что файловый дескриптор 3 не используется " .alsoUseless.sh
Это позволит вам направить вывод вашего
useless.sh
скрипта с помощью такой команды, какsed
и сохранитьstderr
в переменной с именемerror
. Результат канала отправляетсяstdout
для отображения или для передачи в другую команду.Он устанавливает пару дополнительных файловых дескрипторов для управления перенаправлениями, необходимыми для этого.
источник
stderr
иstdout
в переменных?dry_run
функцию, которая может надежно выбирать между отображением своих аргументов и их выполнением, независимо от того, передается ли команда, выполняемая без пробега, в какой-либо другой файл.read
не принимает входные данные из канала. Вы можете использовать другие методы для достижения того, что вы пытаетесь продемонстрировать.Перенаправить stderr в stdout, stdout в / dev / null, а затем использовать обратные клавиши или
$()
для захвата перенаправленного stderr:источник
PY_VERSION="$(python --version 2>&1)"
Для этого вопроса существует множество дубликатов, многие из которых имеют несколько более простой сценарий использования, когда вы не хотите захватывать stderr и stdout и код выхода одновременно.
работает для общего сценария, где вы ожидаете либо правильного вывода в случае успеха, либо диагностического сообщения на stderr в случае сбоя.
Обратите внимание, что управляющие операторы оболочки уже проверяются
$?
под капотом; так что-нибудь, что выглядит какэто просто неуклюжий, однотипный способ сказать
источник
источник
command
это плохой выбор, поскольку на самом деле есть встроенное имя. Можно сделать этоyourCommand
или так, чтобы быть более явным.Для удобства читателя этот рецепт здесь
Если вы хотите поймать
stderr
некоторыеcommand
вvar
вы можете сделатьПосле этого у вас есть все:
Если
command
все просто (а не что-то вродеa | b
), вы можете оставить внутреннее{}
:Обернут в легкую функцию многократного использования
bash
(вероятно, для версии 3 и вышеlocal -n
):Разъяснение:
local -n
псевдонимы "$ 1" (которая является переменной дляcatch-stderr
)3>&1
использует файловый дескриптор 3 для сохранения там стандартных точек{ command; }
(или "$ @") затем выполняет команду в захвате вывода$(..)
2>&1
перенаправляетstderr
на выход захвата$(..)
1>&3
перенаправляетstdout
от захвата вывода$(..)
обратно к «внешнему»,stdout
который был сохранен в файловом дескрипторе 3. Обратите внимание, чтоstderr
все еще относится к тому месту, куда указывал FD 1: на захват вывода$(..)
3>&-
затем закрывает файловый дескриптор 3, так как он больше не нужен, так чтоcommand
внезапно не появляется какой-то неизвестный открытый файловый дескриптор. Обратите внимание, что внешняя оболочка все еще имеет открытый FD 3, ноcommand
не увидит его.lvm
жалуются на неожиданные файловые дескрипторы. Иlvm
жалуетсяstderr
- только то, что мы собираемся захватить!Вы можете поймать любой другой дескриптор файла с этим рецептом, если вы адаптируетесь соответственно. Конечно, кроме файлового дескриптора 1 (здесь логика перенаправления будет неправильной, но для файлового дескриптора 1 вы можете просто использовать
var=$(command)
как обычно).Обратите внимание, что это жертвует файловым дескриптором 3. Если вам понадобится этот файловый дескриптор, смело меняйте номер. Но имейте в виду, что некоторые оболочки (начиная с 1980-х годов) могут восприниматься
99>&1
как аргумент,9
за которым следует9>&1
(это не проблема дляbash
).Также обратите внимание, что не так легко сделать этот FD 3 настраиваемым через переменную. Это делает вещи очень нечитаемыми:
Ноты:
catch-var-from-fd-by-fd var 2 3 cmd..
такой же какcatch-stderr var cmd..
shift || return
это всего лишь способ предотвратить неприятные ошибки, если вы забудете указать правильное количество аргументов. Возможно, завершение оболочки будет другим способом (но это затрудняет тестирование из командной строки).exec
, но тогда она становится действительно уродливой.bash
так хорошо, что нет необходимостиlocal -n
. Однако тогда вы не можете использовать локальные переменные, и это становится ужасно!eval
они используются безопасным образом. Обычноeval
считается опасным. Однако в этом случае это не более зло, чем использование"$@"
(для выполнения произвольных команд). Однако, пожалуйста, не забудьте использовать точное и правильное цитирование, как показано здесь (иначе это становится очень и очень опасным ).источник
Вот как я это сделал:
Пример использования:
Это делает использовать временный файл. Но, по крайней мере, уродливые вещи заключены в функцию.
источник
eval
. Например,printf -v "$1" '%s' "$(<tmpFile)"
не рискует запустить произвольный код, если для вашейTMPDIR
переменной задано вредоносное значение (или имя переменной назначения содержит такое значение).rm -- "$tmpFile"
является более надежным, чемrm $tmpFile
.Это интересная проблема, на которую я надеялся найти элегантное решение. К сожалению, я получаю решение, похожее на мистер Леффлер, но добавлю, что вы можете бесполезно вызывать функцию из Bash для улучшения читабельности:
Все другие виды перенаправления вывода должны быть поддержаны временным файлом.
источник
POSIX
STDERR может быть захвачен некоторой магией перенаправления:
Обратите внимание, что передача STDOUT команды (здесь
ls
) выполняется внутри самой внутренней части{
}
. Если вы выполняете простую команду (например, не трубу), вы можете удалить эти внутренние скобки.Вы не можете передавать по конвейеру за пределы команды, так как конвейер создает подоболочку в
bash
иzsh
, и назначение переменной в подоболочке не будет доступно для текущей оболочки.удар
Во-
bash
первых, было бы лучше не предполагать, что файловый дескриптор 3 не используется:Обратите внимание, что это не работает в
zsh
.Спасибо за этот ответ для общей идеи.
источник
Этот пост помог мне придумать подобное решение для моих собственных целей:
Тогда, пока наше СООБЩЕНИЕ не является пустой строкой, мы передаем его другим материалам. Это даст нам знать, если наш format_logs.py потерпел неудачу с каким-то исключением из Python.
источник
Захват и печать stderr
Сломать
Вы можете использовать
$()
для захвата stdout, но вместо этого вы хотите захватить stderr. Таким образом, вы меняете stdout и stderr. Использование fd 3 в качестве временного хранилища в стандартном алгоритме подкачки.Если вы хотите захватить И распечатать, используйте
tee
для создания дубликата. В этом случае выходные данныеtee
будут записываться,$()
а не переходить на консоль, но stderr (oftee
) будет по-прежнему идти на консоль, поэтому мы используем его в качестве второго вывода дляtee
специального файла,/dev/fd/2
посколькуtee
ожидается путь к файлу, а не fd число.ПРИМЕЧАНИЕ. Это очень много перенаправлений в одну строку, и порядок имеет значение.
$()
захватывает стандартный выводtee
в конце конвейера, а сам конвейер направляет стандартный вывод./useless.sh
в стандартный потокtee
ПОСЛЕ того, как мы поменялись стандартными и стандартными./useless.sh
.Используя стандартный вывод ./useless.sh
ОП сказал, что он все еще хотел использовать (а не только печатать) стандартный вывод
./useless.sh | sed 's/Output/Useless/'
.Нет проблем, просто сделайте это ПЕРЕД обменом stdout и stderr. Я рекомендую переместить его в функцию или файл (также-useless.sh) и вызвать его вместо ./useless.sh в строке выше.
Однако, если вы хотите CAPTURE stdout AND stderr, то я думаю, что вам придется использовать временные файлы, потому что они
$()
будут делать только один файл за раз, и он создает подоболочку, из которой вы не сможете вернуть переменные.источник
Немного повторяя ответ Тома Хейла, я обнаружил, что можно превратить йогу перенаправления в функцию для более легкого повторного использования. Например:
Почти наверняка возможно упростить это дальше. Особенно тщательно не тестировал, но, похоже, он работает как с bash, так и с ksh.
источник
Если вы хотите обойти использование временного файла, вы можете использовать процесс подстановки. Я еще не совсем заставил его работать. Это была моя первая попытка:
Потом я попробовал
тем не мение
Таким образом, подстановка процесса обычно делает правильно ... к сожалению, всякий раз, когда я оборачиваю STDIN
>( )
чем-то внутри,$()
пытаясь передать это переменной, я теряю содержимое$()
. Я думаю, что это потому, что$()
запускает подпроцесс, который больше не имеет доступа к файловому дескриптору в / dev / fd, который принадлежит родительскому процессу.Подстановка процессов позволила мне работать с потоком данных, которого больше нет в STDERR, к сожалению, я, похоже, не могу управлять им так, как мне хочется.
источник
./useless.sh 2> >( ERROR=$( cat <() ); echo "$ERROR" )
вы бы увидели выводERROR
. Проблема в том, что подстановка процесса выполняется в под-оболочке, поэтому значение, установленное в под-оболочке, не влияет на родительскую оболочку.источник
a=> b=>stderr
a
он оценивается и присваивается в под-оболочке, а назначение в под-оболочке не влияет на родительскую оболочку. (Проверено на Ubuntu 14.04 LTS, а также на Mac OS X 10.10.1.)GNU bash, version 4.4.12(1)-release (x86_64-pc-msys)
)SLE 11.4
ни на одном и производит эффект, описанный @JonathanLefflerВ зш:
источник
Для проверки ошибок ваших команд:
Вдохновленный в Бережливом производстве:
источник
if
. Позвольте мне опубликовать отдельное решение.Простое решение
Будет производить:
источник
Улучшение в ответе YellowApple :
Это функция Bash для захвата stderr в любую переменную
stderr_capture_example.sh
:Тестирование:
Вывод:
Эта функция может быть использована для захвата возвращенного выбора
dialog
команды.источник