эхо, которое выводит на stderr

1116

Существует ли стандартный инструмент Bash, который действует как эхо, но выводит на stderr, а не на stdout?

Я знаю, что могу сделать, echo foo 1>&2но это довольно уродливо и, я подозреваю, подвержено ошибкам (например, более вероятно, что редактирование будет неправильным, когда все изменится)

BCS
источник
Связанный: stackoverflow.com/a/12704488/194894
Поток

Ответы:

1454

Вы можете сделать это, что облегчает чтение:

>&2 echo "error"

>&2копирует дескриптор файла № 2 в дескриптор файла № 1. Следовательно, после выполнения этого перенаправления оба дескриптора файла будут ссылаться на один и тот же файл: один дескриптор файла № 2 изначально ссылался на него. Для получения дополнительной информации см. Учебник по перенаправлению Bash Hackers .

Марко Аурелио
источник
2
Я изучил этот трюк довольно давно. На этой странице есть хорошая информация. tldp.org/LDP/abs/html/io-redirection.html
Марко Аурелио
46
@BCS Я не знаю, как использовать aliasскрипт в оболочке. Вероятно, было бы безопаснее использоватьerrcho(){ >&2 echo $@; }
Брэден Бест
3
> & 2 обычно ставится в конце. Это сработает, но его используют реже
Искрен Ивов Чернев,
159
За почти 40 лет, что я использую Unix-подобные системы, мне никогда не приходило в голову, что вы можете поставить перенаправление куда угодно, но в конце. Выступление так, как это, делает это намного более очевидным (или «облегчает чтение», как говорит @MarcoAurelio). +1 за то, что научил меня чему-то новому.
Гефест
К вашему сведению: если вы хотите отформатировать или сделать что-то, кроме простого вывода строки, вам придется переместить перенаправление обратно в конец. Например errcho(){ >&2 echo $@|pr -To5;}, не будет работать. Чтобы сделать что-то подобное, вы должны будете поместить редирект где-то после последней трубы, как:errcho(){ echo $@|>&2 pr -To5;}
Джон Ред
424

Вы можете определить функцию:

echoerr() { echo "$@" 1>&2; }
echoerr hello world

Это будет быстрее, чем скрипт и не будет иметь никаких зависимостей.

В предложении Камило Мартина, касающемся bash, используется «строка здесь», и она напечатает все, что вы ей передадите, включая аргументы (-n), которые echo обычно глотает:

echoerr() { cat <<< "$@" 1>&2; }

Решение Гленна Джекмана также позволяет избежать проблемы глотания аргументов:

echoerr() { printf "%s\n" "$*" >&2; }
Джеймс Рот
источник
7
Я должен сказать, что эхо вроде ненадежно. echoerr -ne xtне собирается печатать "-ne xt". Лучше использовать printfдля этого.
Камило Мартин
10
О, вы тоже можете использовать кошку:echoerr() { cat <<< "$@" 1>&2; }
Камило Мартин,
2
Я не знал об этом. Добавлен.
Джеймс Рот
4
Или,printf "%s\n" "$*" >&2
Гленн Джекман
4
@GKFX Конечно, это работает правильно только при цитировании. Почему люди не цитируют свои строки, это выше моего понимания. (когда вы не $IFSзаключаете в кавычки, все, разделенные одним или несколькими пробелами, отправляются в качестве отдельного аргумента, что в случае echoозначает их конкатенацию с 0x20s, но опасность не заключать в кавычки намного больше, чем удобство ввода на 2 символа меньше) ,
Камило Мартин
252

Поскольку 1это стандартный вывод, вам не нужно явно называть его перед перенаправлением вывода, например> вместо, но вместо этого можно просто набрать:

echo Это сообщение отправляется в stderr> & 2

Поскольку вы, похоже, беспокоитесь о том, что вам 1>&2будет сложно набрать достоверно, устранение лишнего 1может быть для вас небольшим стимулом!

Брэндон Роудс
источник
59

Другой вариант

echo foo >>/dev/stderr
Стивен Пенни
источник
4
Является ли этот вариант портативным? Кто-нибудь знает, если это не работает для какого-то вкуса Unix?
Дакав
7
Это не работает в определенных chroot, которые не могут получить доступ к / dev / stderr.
Захари Вэнс
12
Если скрипт, который выполняет эту строку - давайте назовем ее foo- имеет свой собственный перенаправленный stderr - например, foo >foo.log 2>&1- тогда echo foo >/dev/stderrзакроет весь вывод перед ним. >>следует использовать вместо:echo foo >>/dev/stderr
доша
Точно так же у вас есть /dev/fd/2.
Jbruni
@Dacav это наверняка портативный: /proc/self/fd/2. Смотрите мой ответ ниже :)
Себастьян
32

Нет, это стандартный способ сделать это. Это не должно вызывать ошибок.

Мэтью Флэшен
источник
9
Это не должно вызывать ошибок, но я мог бы с большей вероятностью. ОТО, это не так уж важно.
БКС
6
@Mike DeSimone: Если кто-то еще напутает с кодом, перетасовывает вывод и фактически не знает bash, они могут легко отбросить (или неправильно набрать) 1>&2. Мы все хотели бы, чтобы этого не случилось, но я уверен, что мы все были местами, где это происходит.
Каскабель
2
( echo something 1>&2 ; something else ) > log-> (echo something; cp some junk 1>&2 ; something else) > logОй.
BCS
30
ИМХО, если кто-то напутает с кодом и не знает bash, это может быть наименьшей из ваших проблем.
Майк ДеСимоне
8
Я думаю, что если это может быть проблемой, вы должны начать использовать другой язык: пытаться сделать bash защищенным от ошибок - рискованное предприятие.
интуитивно
17

Если вы не против записать сообщение также в syslog, используйте not_so_ugly:

logger -s $msg

Параметр -s означает: «Вывести сообщение в стандартную ошибку, а также в системный журнал».

Grzegorz Luczywo
источник
1
это замечательно! насколько это портативно?
code_monk
@code_monk: ожидается, что команда logger совместима со стандартом IEEE Std 1003.2 («POSIX.2»). Команда logger является частью пакета util-linux и доступна из архива ядра Linux ⟨kernel.org/pub/linux/utils / util- linux⟩.
Мику
12

Это простая функция STDERR, которая перенаправляет вход канала в STDERR.

#!/bin/bash
# *************************************************************
# This function redirect the pipe input to STDERR.
#
# @param stream
# @return string
#
function STDERR () {

cat - 1>&2

}

# remove the directory /bubu
if rm /bubu 2>/dev/null; then
    echo "Bubu is gone."
else
    echo "Has anyone seen Bubu?" | STDERR
fi


# run the bubu.sh and redirect you output
tux@earth:~$ ./bubu.sh >/tmp/bubu.log 2>/tmp/bubu.err
erselbst
источник
2
Я думаю, что вы можете сделать то же самое с псевдонимом и быть намного более компактным
BCS
или вы можете просто напрямую echo what | /dev/stderr
передать
11

Примечание: я отвечаю на пост, а не на вводящий в заблуждение / расплывчатый вопрос «эхо, которое выводит на stderr» (уже отвеченный OP).

Используйте функцию, чтобы показать намерение и источник реализации, которую вы хотите. Например

#!/bin/bash

[ -x error_handling ] && . error_handling

filename="foobar.txt"
config_error $filename "invalid value!"

output_xml_error "No such account"

debug_output "Skipping cache"

log_error "Timeout downloading archive"

notify_admin "Out of disk space!"

fatal "failed to open logger!"

И error_handlingсущество:

ADMIN_EMAIL=root@localhost

config_error() { filename="$1"; shift; echo "Config error in $filename: $*" 2>&1; }

output_xml_error() { echo "<error>$*</error>" 2>&1; }

debug_output() { [ "$DEBUG"=="1" ] && echo "DEBUG: $*"; }

log_error() { logger -s "$*"; }

fatal() { which logger >/dev/null && logger -s "FATAL: $*" || echo "FATAL: $*"; exit 100; }

notify_admin() { echo "$*" | mail -s "Error from script" "$ADMIN_EMAIL"; }

Причины, которые решают проблемы в OP:

  • лучший синтаксис возможен (значащие слова вместо уродливых символов)
  • сложнее сделать ошибку (особенно если вы повторно используете скрипт)
  • это не стандартный инструмент Bash, но это может быть стандартная библиотека оболочки для вас или вашей компании / организации

Другие причины:

  • ясность - показывает намерение другим сопровождающим
  • скорость - функции быстрее, чем сценарии оболочки
  • возможность повторного использования - функция может вызывать другую функцию
  • конфигурируемость - нет необходимости редактировать оригинальный скрипт
  • отладка - легче найти строку, ответственную за ошибку (особенно, если вы зацикливаетесь на тонне перенаправления / фильтрации выходных данных)
  • Надежность - если функция отсутствует и вы не можете редактировать скрипт, вы можете использовать внешний инструмент с тем же именем (например, log_error может быть связан с logger в Linux)
  • переключение реализаций - вы можете переключиться на внешние инструменты, удалив атрибут "x" библиотеки
  • независимость от вывода - вам больше не нужно заботиться о том, идет ли он в STDERR или где-либо еще
  • персонализация - вы можете настроить поведение с переменными среды
Цезари Багинский
источник
10

Мое предложение:

echo "my errz" >> /proc/self/fd/2

или

echo "my errz" >> /dev/stderr

echo "my errz" > /proc/self/fd/2будет эффективно выводится stderrпотому , что /proc/selfэто ссылка на текущий процесс, и /proc/self/fdдержит процесс открыт дескрипторов файлов, а затем 0, 1и 2стоять stdin, stdoutи stderrсоответственно.

/proc/selfСсылка не работает на MacOS, однако, /proc/self/fd/*доступно на Termux на Android, но не /dev/stderr. Как определить ОС из скрипта Bash? может помочь, если вам нужно сделать ваш скрипт более переносимым, определив, какой вариант использовать.

Себастьян
источник
5
/proc/selfСсылка не работает на MacOS, так что я буду придерживаться более прямолинейный /dev/stderrметод. Кроме того, как отмечено в других ответах / комментариях, вероятно, лучше использовать, >>чтобы добавить.
MarkHu
4
/proc/self/fd/*доступно на Termux на Android, но не доступно /dev/stderr.
go2null
9

Не используйте, так catкак некоторые из них упоминаются здесь. catэто программа while echoи printfвстроенная в bash (shell). Запуск программы или другого скрипта (также упомянутого выше) означает создание нового процесса со всеми его расходами. Используя встроенные функции, написание функций довольно дешево, потому что нет необходимости создавать (выполнять) процесс (-окружение).

Опнер спрашивает "есть ли какой-нибудь стандартный инструмент для вывода ( конвейера ) в stderr", краткий ответ: НЕТ ... почему? ... перераспределение каналов - это элементарная концепция в таких системах, как unix (Linux ...), и bash (sh) опирается на эти концепции.

Я согласен с новичком, что перенаправление с нотациями, как это: &2>1 это не очень приятно для современных программистов, но это bash. Bash не был предназначен для написания огромных и надежных программ, он предназначен для того, чтобы помочь администраторам работать там с меньшим количеством нажатий клавиш ;-)

И, по крайней мере, вы можете разместить перенаправление в любом месте строки:

$ echo This message >&2 goes to stderr 
This message goes to stderr
return42
источник
1
Указание разработчикам не использовать программы только по причине производительности - преждевременная оптимизация. Элегантные, простые в использовании подходы должны быть предпочтительнее сложного для понимания кода, который работает лучше (порядка миллисекунд).
GuyPaddock
@GuyPaddock извините, вы не прочитали это должным образом. Елки; Речь идет о перенаправлении труб, которые хорошо обрабатываются Bash. Если кому-то не нравится (уродливый) синтаксис перенаправления bash, ему следует прекратить реализацию сценариев bash или изучить способ bash. Во- вторых, Вы должны знать, как дорого запускать новый процесс по сравнению с тем, что вы называете встроенным bash.
возврат42
1
Есть разница между тем, чтобы дать кому-то знать компромиссы производительности встроенных модулей Bash catи инструктажем, чтобы кто-то не использовал cat, потому что он медленный. Существует бесчисленное множество случаев, когда cat является правильным выбором, поэтому я возражаю против вашего ответа.
GuyPaddock
@GuyPaddock Открывалка попросила echoзамену. Даже если он использует cat, он должен использовать перенаправление bash. тем не мение. Таким образом, здесь нет абсолютно никакого смысла использовать cat. Кстати, я использую cat100 раз в день, но никогда в контексте, который просил новичок ... вы поняли?
возврат42
8

Другой вариант, на который я недавно наткнулся, это:

    {
        echo "First error line"
        echo "Second error line"
        echo "Third error line"
    } >&2

При этом используются только встроенные средства Bash, в то время как вывод многострочных ошибок менее подвержен ошибкам (так как вам не нужно добавлять &>2каждую строку).

GuyPaddock
источник
1
Не могу поверить, вы голосуете против меня, когда я рекомендую использовать bash-redirect, а в своем ответе вы используете bash-redirect.
возврат42
1
@ return42 Я проголосовал за твой ответ, потому что все, что он сделал, это сказал ОП, что нет лучшего ответа, чем то, с чего они начали ... это не совсем ответ. Я также не вижу в вашем ответе суб-оболочки ... ваш ответ на самом деле просто советует OP не использовать catили какую-либо другую утилиту, которая не относится к теме вопроса.
GuyPaddock
6

read это встроенная команда оболочки, которая печатает в stderr и может использоваться как echo без выполнения трюков перенаправления:

read -t 0.1 -p "This will be sent to stderr"

-t 0.1Является тайм - аут , который отключает Рида основную функциональность, хранения одной строки стандартного ввода в переменную.

Дуглас Мейл
источник
5
Bash на OS X не допускает «0.1»
Джеймс Рот
2

Сделать сценарий

#!/bin/sh
echo $* 1>&2

это было бы вашим инструментом.

Или сделайте функцию, если вы не хотите иметь скрипт в отдельном файле.

n0rd
источник
6
Лучше, чтобы это была функция (как ответ Джеймса Рота), и лучше передавать все аргументы, а не только первые.
Каскабель
2
Почему функция будет лучше? (Или, альтернативно: «Лучше объяснить, почему так будет лучше ...»)
Огр Псалом33
3
@ OgrePsalm33 Одна из причин, по которой функция будет лучше, заключается в том, что при вызове сценария обычно создается новый экземпляр оболочки, чтобы обеспечить среду, в которой выполняется сценарий. Функция, с другой стороны, помещается в текущую среду оболочки. В этом случае вызов функции был бы гораздо более эффективной операцией, поскольку было бы исключено создание другого экземпляра оболочки.
Дестенсон
0

Объединение решения, предложенного Джеймсом Ротом и Гленном Джекманом

  • добавьте код цвета ANSI, чтобы отобразить сообщение об ошибке красным цветом:
echoerr() { printf "\e[31;1m%s\e[0m\n" "$*" >&2; }

# if somehow \e is not working on your terminal, use \u001b instead
# echoerr() { printf "\u001b[31;1m%s\u001b[0m\n" "$*" >&2; }

echoerr "This error message should be RED"
Полимеразная
источник
-10

Mac OS X: я попробовал принятый ответ и несколько других ответов, и все они привели к написанию STDOUT, а не STDERR на моем Mac.

Вот переносимый способ записи в стандартную ошибку с использованием Perl:

echo WARNING! | perl -ne 'print STDERR'
Ноа Суссман
источник
LOL Downvote все, что вы хотите, но это решение, которое я на самом деле использую в своем коде!
Ноа Суссман