Как определить, является ли вывод команды или скрипта командной строки stdout или stderr

32

Допустим, я запускаю команду или сценарий оболочки, и он дает мне вывод. Не зная внутренностей этой команды или сценария оболочки, как определить, был ли вывод получен stderrили stdout?

Например,

$ ls -ld /
drwxrwxr-t  35 root  admin  1258 Dec 11 19:16 /

против

ls -ld /test
ls: /test: No such file or directory

Как мне удостовериться, что первая команда напечатана, stdoutа вторая stderr(сделана?)?

KM.
источник
1
Какую проблему вы пытаетесь решить здесь?
Кенстер
3
Боялся, что кто-то спросит это; на самом деле никого, в основном любопытного и надеющегося улучшить мое понимание перенаправления.
КМ.
7
Вы можете поместить stderredв свою оболочку окружение, LD_PRELOADчтобы получить stdoutи stderrразных цветов. Вот связанный вопрос в этом ключе.
Анко

Ответы:

18

Невозможно сказать, когда результат уже напечатан. В этом случае оба stdoutи stderrподключены к терминалу, поэтому информация о том, в какой поток был записан, была уже потеряна к тому времени, как текст появился на вашем терминале; они были объединены программой прежде, чем когда-либо добирались до терминала.

Что вы можете сделать, в таком случае , как выше, было бы запустить команду с stdoutи stderrперенаправлены в разные места и посмотреть , что происходит. Или запустите его дважды, один раз с stdoutперенаправленным на /dev/nullи один раз с stderrперенаправленным на /dev/null, и посмотрите, какой из этих случаев приводит к появлению текста.

Вы можете перенаправить stdoutк /dev/nullпо лавируя >/dev/nullна конце командной строки, и вы можете перенаправить stderrк /dev/nullпути добавления 2>/dev/null.

godlygeek
источник
9

Вы можете перенаправить стандартный вывод с помощью > fileи перенаправить стандартный вывод с помощью 2> file. Многие современные оболочки поддерживают перенаправление на команды, поэтому вы можете использовать sedдля выделения того, какой вывод поступает из какого потока:

$ ls 2> >(sed 's/^/2: /') > >(sed 's/^/1: /')
1: unity_support_test.0
1: vmwareDnD

$ ls foo 2> >(sed 's/^/2: /') > >(sed 's/^/1: /')
2: ls: cannot access foo: No such file or directory
Марк Плотник
источник
Очень хорошо! Было бы замечательно, если бы можно было выделить строки цветом, а не префиксом.
dotancohen
3
@dotancohen: можно! Например,(echo "this is stdout"; echo "this is stderr" >&2) > >(sed 's/.*/\x1b[32m&\x1b[0m/') 2> >(sed 's/.*/\x1b[31m&\x1b[0m/')
PM 2Ring
Обратите внимание, что если есть смешанный вывод из stdout и stderr, сначала будет напечатан stdout, а затем stderr.
nyuszika7h
5

annotate-outputСкрипт из Debian - х devscriptsпозволяет делать это выборочно:

$ annotate-output ls -ld /test
14:54:22 -: Started ls -ld /test
14:54:22 E: ls: cannot access /test: No such file or directory
14:54:22 -: Finished with exitcode 2

Во втором столбце указаны stdout и stderr с Oи Eсоответственно.

Есть несколько предостережений, главное из которых было отмечено в других ответах: вы не можете сделать это после факта. Ни оболочка, ни терминал не знают, как произвольная программа использует свои файловые дескрипторы, хотя оболочка отвечает за их первоначальную настройку.

Этот метод использует fifo, запись в fifo может вести себя иначе, чем запись в tty, и запись в два разных fifo определенно отличается (потенциальные проблемы синхронизации / чередования). Кроме того, он не подходит для интерактивного использования, например, annotate-output bashэто не очень хороший план, но он полезен для многих других целей. Существует множество примеров сценариев и функций оболочки в ответах на связанные вопросы о раскрашивании stdin / stdout / stderr. Самый надежный из них - это stderrd, который использует модификацию времени выполнения (большинства) программ для изменения данных, записанных в stderr.

На этот вопрос, на который ссылается Anko, есть хорошие ответы по этой связанной теме: раскраска вывода stdout / stderr: могу ли я настроить свою оболочку для печати STDERR и STDOUT разными цветами?

mr.spuratic
источник
1
Обратите внимание, что это bashскрипт, использующий while readциклы и запускающий одну dateкоманду для каждой строки stdout или stderr, поэтому он будет намного менее эффективным, чем cmd > >(ts '%T O:') 2> >(ts '%T E:')эквивалентный.
Стефан
1

Помимо других ответов, интересно указать /proc/$PID/fd(хотя и не отвечает на вопрос):

$ cat > /dev/null 2> /tmp/blablah &
[1] 3073

[1]+  Stopped                 cat > /dev/null 2> /tmp/blablah
$ ls -l /proc/3073/fd
total 0
lrwx------ 1 kampde kampde 64 Feb 24 11:43 0 -> /dev/pts/33
l-wx------ 1 kampde kampde 64 Feb 24 11:43 1 -> /dev/null
l-wx------ 1 kampde kampde 64 Feb 24 11:43 2 -> /tmp/blablah

Как видите, здесь вы можете увидеть файловые дескрипторы, открытые для процесса. 0это STDIN, 1это STDOUTи 2есть STDERR. Если вы не перенаправили STDOUT или STDERR, вы увидите /dev/pts/33(по крайней мере, в этом примере), потому что они будут указывать на терминал.

примечания : /proc/$PIDсуществует только для запущенных процессов. В этом случае я использовал catбез аргументов, поэтому он не заканчивается, пока я не закрою STDIN. Я также выполнил это в фоновом режиме, поэтому у меня есть PID немедленно для примера.

Карлос Кампдеррос
источник
0

Не совсем понятно, о чем вы спрашиваете, но это может помочь

ls -ld /
echo $?    # Exit status 0 returned because command executed successfully.

ls -ld /test
echo $?    # Non-zero exit status returned -- command failed to execute

Источник

Если код выхода равен 0, это просто означает, что команда выполнена правильно (stdout), здесь вы можете найти значения, если код выхода отличается от 0 (stderr)

Клерк
источник
Очень интересно! Я бы никогда не связал статусы выхода с перенаправлением. Означает ли ноль, stdoutа ненулевой stderr?
КМ.
7
@ Км., Нет. Я бы сказал, что это сильно коррелирует, но ничто не мешает программе писать stderrи возвращать хороший код ошибки или писать stdoutи возвращать плохой код ошибки. На самом деле, попробуйте find /root- при условии, что вы не работаете от имени пользователя root, вы должны распечатать две строки - «/ root» будет напечатано stdout, а «find: / root: Permission denied» будет напечатано stderr. И найти возвращает неверный код возврата.
Godlygeek
Проблема ОП ясна в том, как определить, какой из них стандартный или стандартный.
it_is_a_literature
0

Обычно STDERR будет иметь имя программы, добавленное к сообщению с двоеточием.

Пример:

rpm -zq some_utils 
rpm: -zq: unknown option

Vs

rpm -ql some_utils 
package some_utils is not installed
Папу
источник
-1

Для захвата и проверки вывода ошибок:

ls -l test 2>errors
if [ -s errors ]; then echo "There were errors:" && cat errors; fi
rm errors
mlgoth
источник