Farbausgabe von gegabelten Prozessen


7

Ich habe ein Runscript, das einige Prozesse startet und sie in den Hintergrund sendet

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

Alle diese Prozesse werden dann gleichzeitig mit derselben Standardausgabe ausgegeben. Meine Frage: Ist es möglich, die Ausgabe jedes einzelnen gegabelten Prozesses zu färben, so dass zum Beispiel eine von ihnen in Grün und die andere in Rot ausgegeben wird?

Antworten:


2

Sie können dies tun, indem Sie durch einen Filter leiten. Es ist lediglich eine Frage des Hinzufügens geeigneter ANSI-Codes vor und nach jeder Zeile:

http://en.wikipedia.org/wiki/ANSI_escape_sequences#Colors

Ich konnte kein Tool finden, das dies tatsächlich nach ein paar Minuten googelt, was etwas seltsam ist, wenn man bedenkt, wie einfach es wäre, eines zu schreiben.

Hier ist eine Idee mit C:

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

/* std=gnu99 required */

// ANSI reset sequence
#define RESET "\033[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] = "\033[", // 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] != '\0'
                    || 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] != '\0'
                    || 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;
}                                       

Ich denke, das ist ungefähr so ​​effizient, wie Sie es sich vorstellen können. Die write()Notwendigkeit, eine ganze Zeile mit den ANSI-Sequenzen auf einmal zu erstellen - das Testen mit parallelen Gabeln führte zu einer Verschachtelung, wenn die ANSI-Sequenzen und der Pufferinhalt getrennt durchgeführt wurden.

Das muss kompiliert werden, -std=gnu99da getoptes nicht Teil des C99-Standards ist, sondern Teil von GNU. Ich habe dies etwas mit parallelen Gabeln getestet; Diese Quelle, ein Makefile und die Tests befinden sich hier in einem Tarball:

http://cognitivedissonance.ca/cogware/utf8_colorize/utf8_colorize.tar.bz2

Wenn die Anwendung, die Sie mit Protokollen verwenden, einen Standardfehler aufweist, denken Sie daran, dies ebenfalls umzuleiten:

application 2>&1 | utf8-colorize -1 2 &

Die .sh-Dateien im Testverzeichnis enthalten einige Verwendungsbeispiele.


Möchtest du etwas weiter erklären (ich bin ein echter Anfänger in Bash)? Muss ich vor oder nach dem Gabeln die Rohrleitungsanweisung hinzufügen?
user2398029

Vorher dh. mongod | colorfilter & pid_mongo=$!
Goldlöckchen

Ich habe mich entschlossen, ein bisschen herumzublödeln und etwas dafür in C zu codieren. Wenn Sie also nichts anderes finden können, kommen Sie später heute oder morgen wieder und ich werde einen Link / eine Quelle dafür aufkleben. Dafür sollte es wirklich ein einfaches, kompiliertes Tool geben.
Goldlöckchen

Genial. Ich werde sehen, was ich auf meiner Seite tun kann, und freue mich darauf, das mit Ihrer Einstellung zu vergleichen.
user2398029

@ Louism: Okay, ich habe einen Code und einen Link zu einem Tarball
hinzugefügt

5
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/" &

+1 Zum Testen definieren Sie zB cmd1() { while true; do echo foo; sleep 1; done }undcmd2() { while true; do echo bar; sleep 3; done }
l0b0

0

Sie sind wahrscheinlich besser darin, die Protokolle auf bestimmte Ausgabedateien umzuleiten?

Es gibt viele verschiedene Lösungen zum Färben der Ausgabe. Am einfachsten wäre es wahrscheinlich, das grc- Paket zu verwenden.


1
Das Umleiten in separate Dateien ist nicht die beste Option, wenn a) Sie versuchen, etwas in Echtzeit zu beobachten / zu debuggen. b) Sie möchten, dass die Reihenfolge der Ereignisse aus mehreren Prozessen beibehalten wird, ohne dass Zeitstempel in separaten Dateien verglichen werden müssen. Ich würde vermuten, dass das OP einen Front-End-Server in Kombination mit einem Back-End-Datenbankserver (Mongo) testet und sehen möchte, was wann passiert.
Goldlöckchen

0

Eine weitere Option, die Sie mit der integrierten Funktion sh verwenden können: Funktioniert auch mit Asche (Busybox);)

RED=`echo -e '\033[0;31m'`
NC=`echo -e '\033[0m'` # No Color

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

Ich habe mir eine Shell-Funktion geschrieben, um Programme einfach im Hintergrund auszuführen. Dies ist für Busybox 'Asche geschrieben! Funktioniert auch in Bash. Renn einfach:

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

Geben Sie Folgendes in Ihre .bashrc ein

ESC="\033"                                                                      
# 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/" &                                   
}                                  

wie hier zu sehen: http://www.bramschoenmakers.nl/en/node/511.html

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.