Я могу настроить свою оболочку для печати STDERR и STDOUT в различных цветах?

Необходимо использовать kill команда для этого.

Чтобы быть более подробными - необходимо указать правильный сигнал, т.е.

$ kill -TSTP $PID_OF_PROCESS

для приостановки процесса и

$ kill -CONT $PID_OF_PROCESS

для возобновления его. Зарегистрированный здесь.

65
13.04.2017, 15:36
6 ответов

Это - более трудная версия Шоу только stderr на экране, но записи и stdout и stderr в файл.

Приложения, работающие в терминале, используют один канал для общения с ним; приложения имеют два выходных порта, stdout и stderr, но они оба подключены к тому же каналу.

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

  • Объединенный вывод не может быть точно в том же порядке, как будто не было никакого перенаправления. Это вызвано тем, что добавленная обработка на одном из канала занимает (немного) времени, таким образом, цветной канал может быть отложен. Если какая-либо буферизация будет сделана, то разупорядочение будет хуже.
  • Терминалы используют цвет, изменяющий escape-последовательности для определения цвета отображения, например. ␛[31m значит “переключатель для красного переднего плана”. Это означает, что, если некоторый вывод, предназначенный к stdout, прибывает так же, как некоторый вывод для stderr отображается, вывод будет искажен. (Еще хуже, если существует переключение канала посреди escape-последовательности, Вы будете видеть мусор.)

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

Другой возможный подход должен был бы заставить программу производить надлежащие последовательности изменения цвета путем сцепления вокруг всех функций libc, которые звонят write системный вызов в библиотеке, загруженной LD_PRELOAD. См. ответ sickill для существующей реализации или ответ Stéphane Chazelas для смешанного подхода, который усиливает strace.

На практике, если это применимо, я предлагаю перенаправить stderr к stdout и передать по каналу в основанный на шаблоне colorizer, такой как colortail или мультихвост или colorizers специального назначения, такой как colorgcc или colormake.

¹ псевдотерминалы. Каналы не работали бы из-за буферизации: источник мог записать в буфер, который повредит синхронность с colorizer.

32
27.01.2020, 19:32
  • 1
    Не могло бы быть трудно исправить терминальную программу к colourize stderr поток. Кто-то предложил что-то вроде этого в мозговом штурме человечности. –  intuited 02.05.2011, 06:27
  • 2
    @intuited: это потребовало бы исправления каждого эмулятора терминала, с которым Вы хотите, чтобы это работало. Используя LD_PRELOAD обманите для прерывания write вызовы, кажется, являются самыми соответствующими, IMO (но с другой стороны, могли бы быть различия на определенном *, отклоняют разновидности.) –  alex 02.05.2011, 16:24
  • 3
    По крайней мере, на Linux, прерывая write один не работал бы, поскольку большинство приложений не звонит непосредственно, но другая функция из некоторой общей библиотеки (как printf) это назвало бы оригинал write –  Stéphane Chazelas 02.11.2012, 01:02
  • 4
    @StephaneChazelas я думал о сцеплении вокруг write обертка syscall. Это встраивается в других функциях в Glibc? –  Gilles 'SO- stop being evil' 02.11.2012, 01:13
  • 5
    stderred проект, кажется, реализация сцепления write через LD_PRELOAD как Вы описываете. –  Drew Noakes 10.11.2013, 16:41

Вот подтверждение концепции, которое я действительно некоторое время поддерживал.

Это только работает в zsh.

# make standard error red
rederr()
{
    while read -r line
    do
        setcolor $errorcolor
        echo "$line"
        setcolor normal
    done
}

errorcolor=red

errfifo=${TMPDIR:-/tmp}/errfifo.$$
mkfifo $errfifo
# to silence the line telling us what job number the background job is
exec 2>/dev/null
rederr <$errfifo&
errpid=$!
disown %+
exec 2>$errfifo

Это также предполагает, что у Вас есть функция, вызванная setcolor.

Упрощенная версия:

setcolor()
{
    case "$1" in
    red)
        tput setaf 1
        ;;
    normal)
        tput sgr0
        ;;
    esac
}
4
27.01.2020, 19:32
  • 1
    Существует намного более простой способ сделать это: exec 2> >(rederr). Обе версии будут иметь проблемы, которые я упоминаю в своем ответе переупорядочения строк и риска искаженным выводом (особенно с длинными линиями). –  Gilles 'SO- stop being evil' 02.05.2011, 03:38
  • 2
    я попробовал это, и это не работало. –  Mikel 02.05.2011, 03:46
  • 3
    seterr должен был бы быть автономный сценарий, не функция. –  Gilles 'SO- stop being evil' 02.05.2011, 10:09

Посмотрите Hilite Mike Schiraldi, который делает это для одной команды за один раз. Мой собственный поток делает это для целой сессии, но также и имеет много других функций/индивидуальных особенностей, которые Вы не могли бы хотеть.

4
27.01.2020, 19:32

Выезд stderred. Это использует LD_PRELOAD сцепляться с libc write() вызовы, колоризация все stderr вывод, идущий в терминал. (В красном по умолчанию.)

36
27.01.2020, 19:32
  • 1
    Хороший, та библиотека является потрясающей. Реальный вопрос: почему не делает моей операционной системы/, терминал идет, это предварительно установило? ;) –  Naftuli Kay 13.12.2011, 23:51
  • 2
    я принимаю Вас, является автором, который является правильным? Необходимо раскрыть присоединение в этом случае. –  Dmitry Grigoryev 16.12.2015, 13:13

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

И затем, существует другой режим, где терминальному драйверу говорят не отозваться эхом, но приложение на этот раз производит что-то. Приложение (как те, которые используют readline как gdb, удар...), может отправить это на своем stdout или stderr, который будет трудным дифференцироваться от чего-то, что это производит для других вещей, чем реагирование на ввод данных пользователем.

Затем для дифференциации stdout приложения от его stderr существует несколько подходов.

Многие из них включают перенаправление команд stdout и stderr к каналам и тем каналам, считанным приложением для окраски его. Существует две проблемы с этим:

  • После того как stdout больше не является терминалом (как канал вместо этого), многие, приложение имеет тенденцию адаптировать их поведение, чтобы начать буферизовать их вывод, что означает, что производит, будет отображенным в больших блоках.
  • Даже если это - тот же процесс, который обрабатывает два канала, нет никакой гарантии, что порядок, текст, записанный приложением на stdout и stderr, будет сохранен как процесс считывания, не может знать (если существует что-то, чтобы быть считанным от обоих), начать ли читать из канала "stdout" или канала "stderr".

Другой подход должен изменить приложение так, чтобы это действительно окрасило свой stdout и stdin. Это часто не возможно или реалистично сделать.

Затем прием (для динамично связанных приложений) может быть должен угнать (использование $LD_PRELOAD как в ответе sickill) функции вывода, вызванные приложением, чтобы произвести что-то и включать код в них, который выбирает основной цвет на основе того, ли предназначены они для вывода чего-то на stderr или stdout. Однако это означает угонять каждую возможную функцию из библиотеки C и любой другой библиотеки, которая делает a write(2) syscall, непосредственно названный приложением, которое может потенциально закончить тем, что писало что-то на stdout или stderr (printf, помещает, perror...), и даже затем, который может изменить его поведение.

Другой подход мог быть должен использовать приемы PTRACE как strace или gdb сделайте для сцепления каждый раз write(2) системный вызов называют и установил цвет вывода на основе ли write(2) находится на дескрипторе файла 1 или 2.

Однако это - вполне большая вещь сделать.

Прием, с которым я просто играл, должен угнать strace самостоятельно (который делает грязную работу сцепления себя перед каждым системным вызовом) использующий LD_PRELOAD, чтобы сказать этому изменять цвет вывода на основе того, обнаружило ли это a write(2) на fd 1 или 2.

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

Обертка LD_PRELOAD была бы похожа:

#define _GNU_SOURCE
#include <dlfcn.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>

int vfprintf(FILE *outf, const char *fmt, va_list ap)
{
  static int (*orig_vfprintf) (FILE*, const char *, va_list) = 0;
  static int c = 0;
  va_list ap_orig;
  va_copy(ap_orig, ap);
  if (!orig_vfprintf) {
    orig_vfprintf = (int (*) (FILE*, const char *, va_list))
      dlsym (RTLD_NEXT, "vfprintf");
  }

  if (strcmp(fmt, "%ld, ") == 0) {
    int fd = va_arg(ap, long);
    switch (fd) {
    case 2:
      write(2, "\e[31m", 5);
      c = 1;
      break;
    case 1:
      write(2, "\e[32m", 5);
      c = 1;
      break;
    }
  } else if (strcmp(fmt, ") ") == 0) {
    if (c) write(2, "\e[m", 3);
    c = 0;
  }
  return orig_vfprintf(outf, fmt, ap_orig);
}

Затем мы компилируем его с:

cc -Wall -fpic -shared -o wrap.so wrap.c -ldl

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

LD_PRELOAD=/path/to/wrap.so strace -qfo /dev/null -e write -s 0 env -u LD_PRELOAD some-cmd

Вы заметите как, если Вы замените some-cmd с bash, подсказка удара и что Вы вводите, появляется в красном (stderr) в то время как с zsh это появляется в черном цвете (потому что zsh копирует stderr на новый fd для отображения его подсказки и эха).

Это, действительно кажется, работает удивительно хорошо даже на приложения, которые Вы ожидали бы не (как те, которые действительно используют цвета).

Окрашивающий режим производится на stracestderr, который, как предполагается, является терминалом. Если приложение перенаправит свой stdout или stderr, то наш угнанный strace будет продолжать писать окрашивающие escape-последовательности на терминале.

То решение имеет свои ограничения:

  • Свойственные к strace: проблемы производительности, Вы не можете выполнить другие команды PTRACE как strace или gdb в нем, или проблемы setuid/setgid
  • Это окрашивает на основе writes на stdout/stderr каждого отдельного процесса. Так, например, в sh -c 'echo error >&2', error было бы зеленым потому что echo производит его на его stdout (какой sh, перенаправленный к stderr sh, но весь strace видит, является a write(1, "error\n", 6)). И в sh -c 'seq 1000000 | wc', seq делает много или writes к ее stdout, таким образом, обертка закончится вывод много (невидимых) escape-последовательностей к терминалу.
15
27.01.2020, 19:32
  • 1
    Хороший. Были предложения существующих ранее оберток по дублирующемуся вопросу. Я отметил вопрос для слияния так, чтобы Ваш ответ был виден там. –  Gilles 'SO- stop being evil' 02.11.2012, 00:57
  • 2
    Возможно, настраивая выделение синтаксиса энергии? strace $CMD | vim -c ':set syntax=strace' -. –  Pablo A 27.08.2017, 17:46

Некоторое предыдущее обсуждение serverfault.

См. также grc и полезный блог об этом.

1
27.01.2020, 19:32

Теги

Похожие вопросы