Как установить цвет шрифта для STDOUT и STDERR - PullRequest
36 голосов
/ 27 июля 2011

Я хочу различать сообщения STDOUT и STDERR в моем терминале.Если скрипт или команда печатает сообщение в терминале, я хочу различать по цветам;возможно ли это?

(Например, цвет шрифта stderr - красный, а цвет шрифта stdout - синий)
Wed Jul 27 12:36:50 IST 2011

$datee
bash: datee: command not found

$alias ls
alias ls='ls --color=auto -F'

$aliass ls
bash: aliass: command not found

Ответы [ 7 ]

50 голосов
/ 24 апреля 2013

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

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

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

$ color command -program -args

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

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

  • color()... & mdash; Создает функцию bash с именем color.
  • set -o pipefail & mdash; Это опция оболочки, которая сохраняет код возврата ошибки команды, вывод которой передается в другую команду. Это делается в подоболочке, которая создается в скобках, чтобы не изменять параметр pipefail во внешней оболочке.
  • "$@" & mdash; Выполняет аргументы функции как новую команду. "$@" эквивалентно "$1" "$2" ...
  • 2>&1 & mdash; Перенаправляет stderr команды на stdout, чтобы она стала sed stdin.
  • >&3 & mdash; Сокращение для 1>&3, перенаправляет stdout на новый дескриптор временного файла 3. 3 возвращается обратно в stdout позже.
  • sed ... & mdash; Из-за перенаправлений, указанных выше, sed stdin является stderr выполненной команды. Его функция заключается в том, чтобы окружать каждую строку цветовыми кодами.
  • $'...' Конструкция bash, которая заставляет ее понимать символы с обратной косой чертой
  • .* & mdash; Соответствует всей строке.
  • \e[31m & mdash; Экранирующая последовательность ANSI, которая заставляет следующие символы быть красными
  • & & mdash; Символ sed заменяет символ, который расширяется до всей совпадающей строки (в данном случае всей строки).
  • \e[m & mdash; Экранирующая последовательность ANSI, которая сбрасывает цвет.
  • >&2 & mdash; Сокращение для 1>&2, перенаправляет sed stdout на stderr.
  • 3>&1 & mdash; Перенаправляет дескриптор временного файла 3 обратно в stdout.
23 голосов
/ 27 июля 2011

Вот хак, о котором я подумал, и он, кажется, работает:

Учитывая следующие псевдонимы для читабельности:

alias blue='echo -en "\033[36m"'
alias red='echo -en "\033[31m"'
alias formatOutput='while read line; do blue; echo $line; red; done'

Теперь вам нужно сначала установить цвет шрифта в вашемтерминал красный (по умолчанию, который будет использоваться для stderr).Затем выполните вашу команду и направьте стандартный вывод через formatOutput, определенный выше (который просто печатает каждую строку синим цветом, а затем сбрасывает цвет шрифта на красный):

shell$ red
shell$ ls / somenonexistingfile | formatOutput

Приведенная выше команда будет печатать в обоих stderrи stdout, и вы увидите, что линии окрашены по-разному.

Надеюсь, это поможет


ОБНОВЛЕНИЕ:

Чтобы сделать это многоразовым, я положил еговсе в маленьком сценарии:

$ cat bin/run 
#!/bin/bash
echo -en "\033[31m"  ## red
eval $* | while read line; do
    echo -en "\033[36m"  ## blue
    echo $line
    echo -en "\033[31m"  ## red
done
echo -en "\033[0m"  ## reset color

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

$ run yourCommand
12 голосов
/ 24 января 2014

Я окрашиваю stderr в красный цвет, связывая дескриптор файла с пользовательской функцией, которая добавляет цвет ко всему, что проходит через него.Добавьте к подписке на ваш .bashrc:

export COLOR_RED="$(tput setaf 1)"
export COLOR_RESET="$(tput sgr0)"

exec 9>&2
exec 8> >(
    perl -e '$|=1; while(sysread STDIN,$a,9999) {print 
"$ENV{COLOR_RED}$a$ENV{COLOR_RESET}"}'
)
function undirect(){ exec 2>&9; }
function redirect(){ exec 2>&8; } 
trap "redirect;" DEBUG
PROMPT_COMMAND='undirect;'

Так что же происходит?Ловушка отладки выполняется непосредственно до и сразу после выполнения команды.Таким образом, stderr перенаправляется перед выполнением команды для включения красного вывода. PROMPT_COMMAND оценивается перед отображением приглашения, и с этим я восстанавливаю stderr в его нормальное состояние.Это необходимо, потому что PS1 и PS2 (ваша подсказка) печатаются поверх stderr , и мне не нужна красная подсказка.вуаля, красный вывод над stderr !

8 голосов
/ 14 декабря 2011

Вы должны проверить stderred: https://github.com/sickill/stderred

4 голосов
/ 08 июля 2013

Я удивлен, что на самом деле никто не понял, как раскрасить потоки stdio. Это закрасит stderr красным для всей (под) оболочки:

exec 3>&2
exec 2> >(sed -u 's/^\(.*\)$/'$'\e''[31m\1'$'\e''[m/' >&3)

В этом случае &3 будет содержать исходный поток stderr.

Вы не должны передавать никакие команды exec, только перенаправления. В этом особом случае exec заменяет текущие (суб) потоки stdio оболочки на те, которые он получает.

Есть несколько предостережений:

  • Поскольку sed будет постоянно работать в параллельной подоболочке, любой прямой вывод, следующий сразу за записью в цветной stdio, вероятно, будет бить sed до tty.
  • Этот метод использует дескриптор файла FIFO; Узлы FIFO работают только в строках. Если вы не пишете перевод строки в поток, ваш вывод будет буферизироваться до тех пор, пока не встретится новая строка. Это не буферизация на части sed: это то, как работают эти типы файлов.

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

Вы можете выполнить аналогичную обработку для отдельных команд, передав по трубопроводу одну и ту же команду sed с обычным оператором канала (|). Цепные конвейеры выполняются синхронно, поэтому условия гонки не возникают, хотя последняя команда в цепочке конвейеров по умолчанию получает свой собственный подоболочку.

4 голосов
/ 27 июля 2011

Да, это не возможно изначально. Вам придется взломать управление tty (в ядре).

Я как-то закончил какую-то маленькую обертку C, прежде чем увидел другие ответы :-) Может быть глючит, а значения жестко закодированы, не используйте это, кроме как для тестирования.

#include "unistd.h"
#include "stdio.h"
#include <sys/select.h>

int main(int argc, char **argv)
{

char buf[1024];
int pout[2], perr[2];
pipe(pout); pipe(perr);

if (fork()!=0)
{
    close(1); close(2);
    dup2(pout[1],1); dup2(perr[1],2);
    close(pout[1]); close(perr[1]);
    execvp(argv[1], argv+1);
    fprintf(stderr,"exec failed\n");
    return 0;
}

close(pout[1]); close(perr[1]);

while (1)
{
    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(pout[0], &fds);
    FD_SET(perr[0], &fds);
    int max = pout[0] > perr[0] ? pout[0] : perr[0];
    int v = select(max+1, &fds, NULL, NULL, NULL);
    if (FD_ISSET(pout[0], &fds))
    {
        int r;
        r = read(pout[0], buf, 1024);
        if (!r) {close(pout[0]); continue;}
        write(1, "\033[33m", 5);
        write(1, buf, r);
        write(1, "\033[0m", 4);
    }
    if (FD_ISSET(perr[0], &fds))
    {
        int r;
        r = read(perr[0], buf, 1024);
        if (!r) {close(perr[0]); continue;}
        write(2, "\033[31m", 5);
        write(2, buf, r);
        write(2, "\033[0m", 4);
    }

    if (v <= 0) break;
}

return 0;
}

Редактировать: По сравнению с решением оболочки, это будет чаще сохранять порядок строк / символов. (Невозможно быть столь же точным, как прямое чтение tty.) При нажатии ^ C не будет отображаться ужасное сообщение об ошибке, и оно будет правильно работать в этом примере:

./c_color_script sh -c "while true; do (echo -n a; echo -n b 1>&2) done"
0 голосов
/ 02 января 2019

Расширяя ответ, который дал @gospes, я добавил функциональность для распечатки частичных строк, не дожидаясь новой строки, и некоторых комментариев.Позволяет улучшить вывод из wget или набирать текст в интерактивной оболочке.

exec 9>&2
exec 8> >(
    while [ "$r" != "1" ]; do
        # read input, no field separators or backslash escaping, 1/20th second timeout
        IFS='' read -rt 0.05 line
        r=$?
        # if we have input, print the color change control char and what input we have
        if ! [ "${#line}" = "0" ]; then
            echo -ne "\e[1;33m${line}"
        fi
        # end of line detected, print default color control char and newline
        if [ "$r" = "0" ] ; then
            echo -e "\e[0m"
        fi
        # slow infinite loops on unexpected returns - shouldn't happen
        if ! [ "$r" = "0" ] && ! [ "$r" = "142" ]; then
            sleep 0.05
        fi
    done
)
function undirect(){ exec 2>&9; }
function redirect(){ exec 2>&8; }
trap "redirect;" DEBUG
PROMPT_COMMAND='undirect;'

Я использовал жирный желтый (1; 33), но вы можете заменить его любым, красным, например (31) или красным (1).; 33), и я произвольно выбрал 0,05 секунды для повторной проверки конца строк и приостановки на неожиданных кодах возврата (никогда не находил);вероятно, его можно было бы уменьшить или удалить из команды чтения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...