Jak program decyduje, czy mieć kolorowe wydruki?


17

Kiedy wykonuję polecenie z terminala, który drukuje kolorowe wydruki (takie jak lslub gcc), drukowane są kolorowe wydruki. Z mojego zrozumienia, proces faktycznie generuje kody specjalne ANSI , a terminal formatuje kolor.

Jednak jeśli wykonam to samo polecenie w innym procesie (powiedzmy niestandardową aplikację C) i przekieruję dane wyjściowe na własne dane wyjściowe aplikacji, kolory te nie zostaną zachowane.

W jaki sposób program decyduje, czy wydrukować tekst w formacie kolorowym? Czy jest jakaś zmienna środowiskowa?

Odpowiedzi:


25

Większość takich programów domyślnie wyświetla kody kolorów tylko na terminalu; sprawdzają, czy ich dane wyjściowe to TTY, używając isatty(3). Zwykle istnieją opcje zastąpienia tego zachowania: wyłącz kolory we wszystkich przypadkach lub włącz kolory we wszystkich przypadkach. Na grepprzykład dla GNU --color=neverwyłącza kolory i --color=alwayswłącza je.

W powłoce możesz wykonać ten sam test za pomocą -t testoperatora: [ -t 1 ]odniesie sukces tylko wtedy, gdy standardowym wyjściem jest terminal.


Czy jest jakiś sposób na oszukanie uruchomionej aplikacji, że proces jest tty?
Chris Smith,

4
Pytanie i odpowiedź już na unix.stackexchange.com/questions/249723 , chris13523. Nawiasem mówiąc, komentarze tak naprawdę nie są miejscem na kolejne pytania.
JdeBP

1
@ chris13524 patrz link JdeBP; w wielu przypadkach możesz również wymusić na programach wyświetlanie kodów kolorów (patrz moja zaktualizowana odpowiedź).
Stephen Kitt

13

Czy jest jakaś zmienna środowiskowa?

Tak. Jest to TERMzmienna środowiskowa. Jest tak, ponieważ istnieje kilka rzeczy, które są wykorzystywane w ramach procesu decyzyjnego.

Trudno tu uogólnić, ponieważ nie wszystkie programy zgadzają się co do schematu pojedynczej decyzji. W rzeczywistości GNU grep, wspomniane w odpowiedzi M. Kitt, jest dobrym przykładem wartości odstającej, która wykorzystuje nieco nietypowy proces decyzyjny z nieoczekiwanymi wynikami. Dlatego bardzo ogólnie:

  • Standardowe wyjście musi być urządzeniem końcowym, jak określono przez isatty().
  • Program musi być w stanie wyszukać rekord typu terminala w bazie danych termcap / terminfo.
  • Dlatego musi istnieć typ terminala do wyszukiwania. TERMZmienna musi istnieć, a jego wartość musi odpowiadać rekord bazy danych.
  • Dlatego musi istnieć baza danych terminfo / termcap. W niektórych implementacjach podsystemu lokalizację bazy danych termcap można określić za pomocą TERMCAPzmiennej środowiskowej. Tak więc na niektórych implementacjach jest sekunda zmienna środowiskowa.
  • Rekord termcap / terminfo musi stwierdzać, że typ terminala obsługuje kolory. W max_colorsterminfo znajduje się pole. Nie jest ustawiony dla typów terminali, które tak naprawdę nie mają możliwości kolorowania. Rzeczywiście, istnieje konwencja terminfo, która dla każdego koloryzowalnego typu terminala zawiera inny rekord z nazwą -mlub -monodołączony do nazwy, który nie określa możliwości kolorystycznych.
  • Rekord termcap / terminfo musi umożliwiać programowi zmianę kolorów. W terminfo znajdują się set_a_foregroundi set_a_backgroundpola.

Jest to nieco bardziej skomplikowane niż tylko sprawdzanie isatty(). Wykonany jest dodatkowo skomplikowana przez kilka rzeczy:

  • Niektóre aplikacje dodają opcje wiersza polecenia lub flagi konfiguracji, które zastępują isatty()sprawdzenie, tak że program zawsze lub nigdy nie zakłada, że ​​ma (wyjściowy) terminal jako wyjście. Dla przykładów:
    • GNU lsma--color opcję wiersza poleceń.
    • BSD lssprawdza zmienne środowiskowe CLICOLOR(jego brak oznacza nigdy ) i CLICOLOR_FORCE(jego obecność zawsze oznacza ), a także obsługuje -Gopcję wiersza polecenia.
  • Niektóre aplikacje nie używają termcap / terminfo i mają wbudowane odpowiedzi na wartość TERM.
  • Nie wszystkie terminale używają sekwencji SGR ECMA-48 lub ISO 8613-6, które są nieco błędnie nazywane „sekwencjami ucieczki ANSI” do zmiany kolorów. Mechanizm termcap / terminfo jest tak naprawdę zaprojektowany, aby odizolować aplikacje od bezpośredniej wiedzy o dokładnych sekwencjach kontrolnych. (Co więcej, należy argumentować, że nikt nie używa sekwencji SGR ISO 8613-6, ponieważ wszyscy zgadzają się co do błędu polegającego na używaniu średnika jako ogranicznika dla sekwencji RGB SGR kolorów. Standard faktycznie określa dwukropek.)

Jak wspomniano, GNU grepfaktycznie wykazuje niektóre z tych dodatkowych złożoności. Nie konsultuje termcap / terminfo, zapisuje sekwencje kontrolne do emisji i zapisuje odpowiedź na TERMzmienną środowiskową.

Jego port Linux / Unix ma ten kod , który umożliwia kolorowanie tylko wtedy, gdy TERMistnieje zmienna środowiskowa, a jej wartość nie jest zgodna z nazwą przewodową dumb:

int
should_colorize (void)
{
  char const * t = getenv („TERM”);
  return t && strcmp (t, „głupi”)! = 0;
}

Więc nawet jeśli TERMtak xterm-mono, GNU grepzdecyduje się na emisję kolorów, nawet jeśli inne programy tego vimnie zrobią.

Jego port Win32 ma ten kod , który umożliwia kolorowanie, gdy TERMzmienna środowiskowa nie istnieje lub gdy istnieje, a jej wartość nie jest zgodna z nazwą przewodową dumb:

int
should_colorize (void)
{
  char const * t = getenv („TERM”);
  powrót ! (t && strcmp (t, „głupi”) == 0);
}

grepProblemy GNU z kolorem

grepKolorystyka GNU jest w rzeczywistości znana. Ponieważ tak naprawdę nie wykonuje właściwej pracy konstruowania danych wyjściowych terminala, a jedynie obwinia w kilku przewodowych sekwencjach sterujących w różnych punktach danych wyjściowych, w próżnej nadziei, że jest to wystarczająco dobre, w rzeczywistości wyświetla nieprawidłowe dane wyjściowe w pewnych okolicznościach.

W tych okolicznościach musi koloryzować coś, co znajduje się na prawym marginesie terminalu. Programy, które prawidłowo wykonują wyjście terminala, muszą uwzględniać automatyczne marginesy automatyczne. Oprócz niewielkiej możliwości, że terminal może ich nie mieć (tzn. auto_right_marginPole w terminfo), zachowanie terminali, które mają automatyczne marginesy, często jest zgodne z precedensem DEC VT oczekującego zawijania linii . GNU grepnie bierze tego pod uwagę, naiwnie oczekując natychmiastowego zawinięcia linii , a jego kolorowe wyjście nie działa .

Kolorowe wydruki nie są prostą sprawą.

Dalsza lektura


2
Jak rozumiem, OP pyta o zmianę w zachowaniu, gdy wyjście jest przekierowywane; $TERMnie wyjaśnia tego. (Twoja odpowiedź jest ogólnie interesująca, ale nie sądzę, że odnosi się do pytania ...)
Stephen Kitt

bardzo interesujące. Chciałem takiego przeglądu na temat tego, jak programy odkrywają (lub po prostu „decydują”), jakie są możliwości terminali od kilku miesięcy. Daje to również wgląd, dlaczego tak trudno jest znaleźć taki przegląd - ponieważ każdy program wydaje się robić to nieco inaczej.
the_velour_fog

Wyjaśnienie, co oznacza oczekujące zawijanie linii i natychmiastowe zawijanie linii, wraz z przykładem pokazującym różnicę byłoby miło.
mosvy

0

unbufferPolecenie z oczekiwać Package de-pary wyjście z pierwszego programu i wejście do drugiego programu.

Użyłbyś tego w ten sposób:

unbuffer myshellscript.sh | grep value

Używam go cały czas z ansible i homebrewed skryptu ctee, dzięki czemu mogę zobaczyć kolor wyjściowy na terminalu, pozostawiając plik dziennika z normalnym (niekolorowanym) wyjściem.

unbuffer ansible-playbook myplaybook.yml | ctee /var/log/ansible/run-$( date "+%F" ).log
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.