Saída de coloração de processos bifurcados

5

Eu tenho um runscript que inicia alguns processos e os envia para o plano de fundo

mongod       & pid_mongo=$!
redis-server & pid_redis=$!
# etc.

Todos esses processos são exportados simultaneamente para a mesma saída padrão. Minha pergunta: é possível colorir a saída de cada processo bifurcado diferente, de modo que - por exemplo - um deles produza verde e o outro fique vermelho?

    
por user2398029 14.12.2012 / 17:06

4 respostas

2

Você poderia fazer isso passando um filtro, é apenas uma questão de adicionar códigos ANSI apropriados antes e depois de cada linha:

link

Não consegui encontrar uma ferramenta que realmente faça isso depois de alguns minutos pesquisando, o que é um pouco estranho, considerando como seria fácil escrever uma.

Aqui está uma ideia usando o C:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>

/* std=gnu99 required */

// ANSI reset sequence
#define RESET "3[0m\n"
// length of RESET
#define RLEN 5
// size for read buffer
#define BUFSZ 16384
// max length of start sequence
#define START_MAX 12

void usage (const char *name) {
    printf("Usage: %s [-1 N -2 N -b -e | -h]\n", name);
    puts("-1 is the foreground color, -2 is the background.\n"
        "'N' is one of the numbers below, corresponding to a color\n"
        "(if your terminal is not using the standard palette, these may be different):\n"
        "\t0 black\n"
        "\t1 red\n"
        "\t2 green\n"
        "\t3 yellow\n"
        "\t4 blue\n"
        "\t5 magenta\n"
        "\t6 cyan\n"
        "\t7 white\n"
        "-b sets the foreground to be brighter/bolder.\n"
        "-e will print to standard error instead of standard out.\n"
        "-h will print this message.\n"
    );
    exit (1);
}


// adds character in place and increments pointer
void appendChar (char **end, char c) {
    *(*end) = c;
    (*end)++;
}


int main (int argc, char *const argv[]) {
// no point in no arguments...
    if (argc < 2) usage(argv[0]);

// process options
    const char options[]="1:2:beh";
    int opt,
        set = 0,
        output = STDOUT_FILENO;
    char line[BUFSZ] = "3[", // ANSI escape
        *p = &line[2];

    // loop thru options
    while ((opt = getopt(argc, argv, options)) > 0) {
        if (p - line > START_MAX) usage(argv[0]);
        switch (opt) {
            case '?': usage(argv[0]);
            case '1': // foreground color
                if (
                    optarg[1] != '
application 2>&1 | utf8-colorize -1 2 &
' || optarg[0] < '0' || optarg[0] > '7' ) usage(argv[0]); if (set) appendChar(&p, ';'); appendChar(&p, '3'); appendChar(&p, optarg[0]); set = 1; break; case '2': // background color if ( optarg[1] != '
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>

/* std=gnu99 required */

// ANSI reset sequence
#define RESET "3[0m\n"
// length of RESET
#define RLEN 5
// size for read buffer
#define BUFSZ 16384
// max length of start sequence
#define START_MAX 12

void usage (const char *name) {
    printf("Usage: %s [-1 N -2 N -b -e | -h]\n", name);
    puts("-1 is the foreground color, -2 is the background.\n"
        "'N' is one of the numbers below, corresponding to a color\n"
        "(if your terminal is not using the standard palette, these may be different):\n"
        "\t0 black\n"
        "\t1 red\n"
        "\t2 green\n"
        "\t3 yellow\n"
        "\t4 blue\n"
        "\t5 magenta\n"
        "\t6 cyan\n"
        "\t7 white\n"
        "-b sets the foreground to be brighter/bolder.\n"
        "-e will print to standard error instead of standard out.\n"
        "-h will print this message.\n"
    );
    exit (1);
}


// adds character in place and increments pointer
void appendChar (char **end, char c) {
    *(*end) = c;
    (*end)++;
}


int main (int argc, char *const argv[]) {
// no point in no arguments...
    if (argc < 2) usage(argv[0]);

// process options
    const char options[]="1:2:beh";
    int opt,
        set = 0,
        output = STDOUT_FILENO;
    char line[BUFSZ] = "3[", // ANSI escape
        *p = &line[2];

    // loop thru options
    while ((opt = getopt(argc, argv, options)) > 0) {
        if (p - line > START_MAX) usage(argv[0]);
        switch (opt) {
            case '?': usage(argv[0]);
            case '1': // foreground color
                if (
                    optarg[1] != '
application 2>&1 | utf8-colorize -1 2 &
' || optarg[0] < '0' || optarg[0] > '7' ) usage(argv[0]); if (set) appendChar(&p, ';'); appendChar(&p, '3'); appendChar(&p, optarg[0]); set = 1; break; case '2': // background color if ( optarg[1] != '%pre%' || optarg[0] < '0' || optarg[0] > '7' ) usage(argv[0]); if (set) appendChar(&p, ';'); appendChar(&p, '4'); appendChar(&p, optarg[0]); set = 1; break; case 'b': // set bright/bold if (set) appendChar(&p, ';'); appendChar(&p, '1'); set = 1; break; case 'e': // use stderr output = STDERR_FILENO; break; case 'h': usage(argv[0]); default: usage(argv[0]); } } // finish 'start' sequence appendChar(&p, 'm'); // main loop // set non-block on input descriptor int flags = fcntl(STDIN_FILENO, F_GETFL, 0); fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); // len of start sequence const size_t slen = p - line, // max length of data to read rmax = BUFSZ - (slen + RLEN); // actual amount of data read ssize_t r; // index of current position in output line size_t cur = slen; // read buffer char buffer[rmax]; while ((r = read(STDIN_FILENO, buffer, rmax))) { if (!r) break; // EOF if (r < 1) { if (errno == EAGAIN) continue; break; // done, error } // loop thru input chunk byte by byte // this is all fine for utf-8 for (int i = 0; i < r; i++) { if (buffer[i] == '\n' || cur == rmax) { // append reset sequence for (int j = 0; j < RLEN; j++) line[j+cur] = RESET[j]; // write out start sequence + buffer + reset write(output, line, cur+RLEN); cur = slen; } else line[cur++] = buffer[i]; } } // write out any buffered data if (cur > slen) { for (int j = 0; j < RLEN; j++) line[j+cur] = RESET[j]; write(output, line, cur+RLEN); } // flush fsync(output); // the end return r; }
' || optarg[0] < '0' || optarg[0] > '7' ) usage(argv[0]); if (set) appendChar(&p, ';'); appendChar(&p, '4'); appendChar(&p, optarg[0]); set = 1; break; case 'b': // set bright/bold if (set) appendChar(&p, ';'); appendChar(&p, '1'); set = 1; break; case 'e': // use stderr output = STDERR_FILENO; break; case 'h': usage(argv[0]); default: usage(argv[0]); } } // finish 'start' sequence appendChar(&p, 'm'); // main loop // set non-block on input descriptor int flags = fcntl(STDIN_FILENO, F_GETFL, 0); fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); // len of start sequence const size_t slen = p - line, // max length of data to read rmax = BUFSZ - (slen + RLEN); // actual amount of data read ssize_t r; // index of current position in output line size_t cur = slen; // read buffer char buffer[rmax]; while ((r = read(STDIN_FILENO, buffer, rmax))) { if (!r) break; // EOF if (r < 1) { if (errno == EAGAIN) continue; break; // done, error } // loop thru input chunk byte by byte // this is all fine for utf-8 for (int i = 0; i < r; i++) { if (buffer[i] == '\n' || cur == rmax) { // append reset sequence for (int j = 0; j < RLEN; j++) line[j+cur] = RESET[j]; // write out start sequence + buffer + reset write(output, line, cur+RLEN); cur = slen; } else line[cur++] = buffer[i]; } } // write out any buffered data if (cur > slen) { for (int j = 0; j < RLEN; j++) line[j+cur] = RESET[j]; write(output, line, cur+RLEN); } // flush fsync(output); // the end return r; }

Acho que é tão eficiente quanto você vai conseguir. O write() precisa fazer uma linha inteira com as seqüências ANSI de uma só vez - testando isso com forquilhas paralelas levadas a intercalar se as sequências ANSI e o conteúdo do buffer fossem feitos separadamente.

Isso precisa ser compilado -std=gnu99 , pois getopt não faz parte do padrão C99, mas faz parte do GNU. Eu testei isso um pouco com garfos paralelos; essa fonte, um makefile e os testes estão em um tarball aqui:

link

Se o aplicativo que você usa com logs para erro padrão, lembre-se de redirecionar também:

%pre%

Os arquivos .sh no diretório de teste contêm alguns exemplos de uso.

    
por 14.12.2012 / 17:53
4
red=$(tput setaf 1)
green=$(tput setaf 2)
default=$(tput sgr0)
cmd1 2>&1 | sed "s/.*/$red&$default/" &
cmd2 2>&1 | sed "s/.*/$green&$default/" &
    
por 16.12.2012 / 20:35
0

Provavelmente, é melhor redirecionar os logs para arquivos de saída específicos?

Existem muitas soluções diferentes para a saída de cores. O mais fácil provavelmente seria usar o pacote grc .

    
por 14.12.2012 / 22:05
0

outra opção que você pode usar com a função sh embutida: funciona também em ash (busybox);)

RED='echo -e '3[0;31m''
NC='echo -e '3[0m'' # No Color

cmdx 2>&1 | sed "s/.*/$RED&$NC/" &

Eu escrevi a mim mesmo uma função de shell para executar facilmente programas em segundo plano. Isso é escrito por busybox 'ash! Trabalha também em bash. Basta executar:

bg_red <whatever cmd you want to run in the bg>

coloque o seguinte no seu .bashrc

ESC="3"                                                                      
# Colors:
COLOR_RED_F="${ESC}[31m"
COLOR_GREEN_F="${ESC}[32m" 
COLOR_RESET="${ESC}[0m" 

bg_red()            
{                                                                               
   [ "$#" -lt "1" ] && echo "bg_red() <cmd to run in bg>" && return 1           
   PIPE_RED='echo -e $COLOR_RED_F'                                              
   PIPE_NC='echo -e $COLOR_RESET'                                               
   $@ 2>&1 | sed "s/.*/$PIPE_RED&$PIPE_NC/" &                                   
}                                  

como visto aqui: link

    
por 23.09.2015 / 16:50