Баш: печать stderr в красный цвет

123

Есть ли способ заставить сообщения bash отображать stderr красным цветом?

kolypto
источник
4
Я предполагаю, что bash никогда не раскрасит свой вывод: некоторые программы могут захотеть что-то проанализировать, а раскраска испортит данные с помощью экранированных последовательностей. Я думаю, приложение с графическим интерфейсом должно обрабатывать цвета.
Колыпто
Сочетание Balázs Pozsár и killdash9 дает четкий ответ: function color { "$@" 2> >(sed $'s,.*,\e[31m&\e[m,') } работает для bash и zsh. Не могу добавить это как ответ б / у репутации.
Генрих Хартманн
1
Я жду ответа, который модифицирует bash для этого. Решения, приведенные ниже, на самом деле модифицируют stderr и, возможно, даже переупорядочивают его в stdout, который ломает вещи, когда должна быть сохранена точная последовательность байтов stderr, например, при конвейерной передаче.
masterxilo

Ответы:

98
command 2> >(while read line; do echo -e "\e[01;31m$line\e[0m" >&2; done)
Balázs Pozsár
источник
6
Большой! Но мне интересно, есть ли способ сделать его постоянным :)
kolypto
9
Отличный совет! Предложение: добавляя >&2прямо перед тем ; done), вывод, предназначенный для stderr, фактически записывается в stderr. Это полезно, если вы хотите захватить нормальный вывод программы.
Хенко
8
Следующее использование tput, и немного более читабельное по моему мнению:command 2> >(while read line; do echo -e "$(tput setaf 1)$line$(tput sgr0)" >&2; done)
Стефан Ласевский
2
Я думаю, что выполнение 2 процессов tput для каждой строки вывода не является элегантным. Возможно, если бы вы сохранили выходные данные команд tput в переменной и использовали их для каждого эха. Но опять же, читаемость не очень лучше.
Balázs Pozsár
1
Это решение не сохраняет пробелы, но мне нравится его краткость. IFS= read -r lineдолжен помочь, но не помогает. Не уверен почему.
Макс Мерфи
89

Метод 1: Используйте процесс замены:

command 2> >(sed $'s,.*,\e[31m&\e[m,'>&2)

Способ 2: создать функцию в скрипте bash:

color()(set -o pipefail;"$@" 2>&1>&3|sed $'s,.*,\e[31m&\e[m,'>&2)3>&1

Используйте это так:

$ color command

Оба метода покажут команду stderrкрасным цветом.

Продолжайте читать, чтобы объяснить, как работает метод 2. Эта команда демонстрирует некоторые интересные особенности.

  • color()... - Создает функцию bash с именем color.
  • set -o pipefail- Это опция оболочки, которая сохраняет код возврата ошибки команды, вывод которой передается в другую команду. Это делается в подоболочке, которая создается в скобках, чтобы не изменять параметр pipefail во внешней оболочке.
  • "$@"- Выполняет аргументы функции как новую команду. "$@"эквивалентно"$1" "$2" ...
  • 2>&1- Перенаправляет stderrкоманду stdoutтак, чтобы она стала sed's stdin.
  • >&3- Сокращение для 1>&3, это перенаправляет stdoutк новому временному дескриптору файла 3. 3возвращается в stdoutбудущее.
  • sed ...- Из-за перенаправлений выше, sed's stdinявляется stderrиз выполненной команды. Его функция заключается в том, чтобы окружать каждую строку цветовыми кодами.
  • $'...' Конструкция bash, которая заставляет его понимать символы с обратной косой чертой
  • .* - Соответствует всей линии.
  • \e[31m - escape-последовательность ANSI, которая заставляет следующие символы быть красными
  • &- Символ sedзамены, который раскрывается до всей совпадающей строки (в данном случае всей строки).
  • \e[m - escape-последовательность ANSI, которая сбрасывает цвет.
  • >&2- стенография для 1>&2, это перенаправляет sed«s stdoutк stderr.
  • 3>&1- Перенаправляет дескриптор временного файла 3обратно в stdout.
killdash9
источник
9
+1 лучший ответ! абсолютно недооцененный!
Muhqu
3
Отличный ответ и еще лучшее объяснение
Даниэль Серодио
Зачем вам нужно делать все дополнительные перенаправления? кажется излишним
qodeninja
1
Есть ли способ заставить его работать zsh?
Эяль Левин
1
ZSH не распознает сокращенные формы перенаправления. Ему просто нужны еще две единицы, то есть:zsh: color()(set -o pipefail;"$@" 2>&1 1>&3|sed $'s,.*,\e[31m&\e[m,'1>&2)3>&1
Rekin
26

Вы также можете проверить stderred: https://github.com/sickill/stderred

sickill
источник
Вау, эта утилита великолепна, единственное, что ей нужно, - это иметь apt-репозиторий, который устанавливает его для всех пользователей, с одной строкой, без необходимости выполнять дополнительную работу для его включения.
Сорин
Казалось, хорошо работает, когда я тестировал его со скриптом сборки в отдельном терминале, но я не решаюсь использовать его глобально (в .bashrc). Спасибо хоть!
Джоэл Пурра
2
В OS X El Capitan способ, которым это работает (DYLD_INSERT_LIBRARIES), "сломан" в двоичных файлах системы, потому что они защищены SIP. Так что может быть лучше использовать параметры bash, приведенные в других ответах.
hmijail
1
@hmijail для MacOS, пожалуйста, следуйте github.com/sickill/stderred/issues/60, чтобы мы могли найти обходной путь, частичный уже существует, но немного глючит.
сорин
15

Bash способ сделать stderr постоянно красным - использовать exec для перенаправления потоков. Добавьте следующее в ваш bashrc:

exec 9>&2
exec 8> >(
    while IFS='' read -r line || [ -n "$line" ]; do
       echo -e "\033[31m${line}\033[0m"
    done
)
function undirect(){ exec 2>&9; }
function redirect(){ exec 2>&8; }
trap "redirect;" DEBUG
PROMPT_COMMAND='undirect;'

Я уже писал об этом ранее: Как установить цвет шрифта для STDOUT и STDERR

gospes
источник
связанные: unix.stackexchange.com/questions/367636/…
phil294
1
Это лучший ответ на сегодняшний день; Легко реализовать без установки / не требуя привилегий sudo, и может быть распространено на все команды.
Люк Дэвис
1
К сожалению, это плохо сочетается с цепочкой команд (команда && nextCommand || errorHandlerCommand). Вывод ошибки идет после вывода errorHandlerCommand.
carlin.scott
1
Точно так же, если я source ~/.bashrcдважды с этим, мой терминал в основном блокируется.
Dolph
@Dolf: В моем bashrc я легко защищаюсь от этого с помощью оператора if, чтобы предотвратить перезагрузку этого кода. В противном случае проблема заключается в перенаправлении 'exec 9> & 2' после того, как перенаправление уже произошло. Возможно, измените его на константу, если вы знаете, где> 2 указывает изначально.
Господи
7

Я сделал скрипт-обертку, который реализует ответ Balázs Pozsár на чистом bash. Сохраните его в ваших командах $ PATH и префиксе, чтобы раскрасить их вывод.

    #! / Bin / Баш

    if [$ 1 == "--help"]; тогда
        echo "Выполняет команду и раскрашивает все возникшие ошибки"
        echo "Пример:` basename $ {0} `wget ..."
        echo "(c) o_O Tync, ICQ # 1227-700, наслаждайтесь!"
        выход 0
        фи

    # Временной файл для перехвата всех ошибок
    TMP_ERRS = $ (Mktemp)

    # Выполнить команду
    "$ @" 2>> (во время чтения строки; выполните echo -e "\ e [01; 31m $ line \ e [0m" | tee --append $ TMP_ERRS; готово)
    EXIT_CODE = $?

    # Показать все ошибки снова
    if [-s "$ TMP_ERRS"]; тогда
        echo -e "\ n \ n \ n \ e [01; 31m === ОШИБКИ === \ e [0m"
        кошка $ TMP_ERRS
        фи
    rm -f $ TMP_ERRS

    # Конец
    выйти из $ EXIT_CODE

kolypto
источник
2
Это может быть сделано более эффективно, если после слова «выполнено» поставить «| tee ...».
Джулиано
3

Вы можете использовать такую ​​функцию


 #!/bin/sh

color() {
      printf '\033[%sm%s\033[m\n' "$@"
      # usage color "31;5" "string"
      # 0 default
      # 5 blink, 1 strong, 4 underlined
      # fg: 31 red,  32 green, 33 yellow, 34 blue, 35 purple, 36 cyan, 37 white
      # bg: 40 black, 41 red, 44 blue, 45 purple
      }
string="Hello world!"
color '31;1' "$string" >&2

Я добавляю> & 2 для печати в stderr

Али Мезгани
источник
4
Не решая проблему. Вы не указали способ отделения stderr от stdout, и это интересует OP.
Джереми Виссер
1

У меня есть слегка измененная версия сценария О_о Тюнка. Мне нужно было сделать эти моды для OS X Lion, и они не идеальны, потому что сценарий иногда завершается раньше, чем обернутая команда. Я добавил сон, но я уверен, что есть лучший способ.

#!/bin/bash

   if [ $1 == "--help" ] ; then
       echo "Executes a command and colorizes all errors occured"
       echo "Example: `basename ${0}` wget ..."
       echo "(c) o_O Tync, ICQ# 1227-700, Enjoy!"
       exit 0
       fi

   # Temp file to catch all errors
   TMP_ERRS=`mktemp /tmp/temperr.XXXXXX` || exit 1

   # Execute command
   "$@" 2> >(while read line; do echo -e "$(tput setaf 1)$line\n" | tee -a $TMP_ERRS; done)
   EXIT_CODE=$?

   sleep 1
   # Display all errors again
   if [ -s "$TMP_ERRS" ] ; then
       echo -e "\n\n\n$(tput setaf 1) === ERRORS === "
       cat $TMP_ERRS
   else
       echo "No errors collected in $TMP_ERRS"
   fi
   rm -f $TMP_ERRS

   # Finish
   exit $EXIT_CODE
утес
источник
1

Это решение сработало для меня: https://superuser.com/questions/28869/immediately-tell-which-output-was-sent-to-stderr

Я поместил эту функцию в мой .bashrcили .zshrc:

# Red STDERR
# rse <command string>
function rse()
{
    # We need to wrap each phrase of the command in quotes to preserve arguments that contain whitespace
    # Execute the command, swap STDOUT and STDERR, colour STDOUT, swap back
    ((eval $(for phrase in "$@"; do echo -n "'$phrase' "; done)) 3>&1 1>&2 2>&3 | sed -e "s/^\(.*\)$/$(echo -en \\033)[31;1m\1$(echo -en \\033)[0m/") 3>&1 1>&2 2>&3
}

Тогда, например:

$ rse cat non_existing_file.txt

даст мне красный вывод.

Эяль Левин
источник
Вы можете добавить set -o pipefail;ранее (evalдля кода выхода перенаправления
kvaps
также добавьте "eval для сохранения пробелов в аргументах
kvaps,
1

используя xargs и printf:

command 2> >(xargs -0 printf "\e[31m%s\e[m" >&2)
Карлос Барселлос
источник
0

версия с использованием fifos

mkfifo errs
stdbuf -o0 -e0 -i0 grep . foo | while read line; do echo -e "\e[01;31m$line  \e[0m" >&2; done &
stdbuf -o0 -e0 -i0 sh $script 2>errs
untore
источник