プログラムは、カラー出力を行うかどうかをどのように決定しますか?


17

カラー出力(lsまたはなどgcc)を印刷する端末からコマンドを実行すると、カラー出力が印刷されます。私の理解では、プロセスは実際にANSIエスケープコードを出力しており、端末は色をフォーマットしています。

ただし、別のプロセス(たとえば、カスタムCアプリケーション)で同じコマンドを実行し、出力をアプリケーションの独自の出力にリダイレクトすると、これらの色は持続しません。

プログラムは、テキストをカラー形式で出力するかどうかをどのように決定しますか?環境変数はありますか?

回答:


25

そのようなプログラムのほとんどは、デフォルトでは端末にカラーコードのみを出力します。を使用して、出力がTTYかどうかを確認しますisatty(3)。通常、この動作をオーバーライドするオプションがあります。すべての場合で色を無効にするか、すべての場合で色を有効にします。たとえば、GNU grepの場合、--color=never色を無効にして--color=always有効にします。

シェルでは、-t test演算子を使用して同じテストを実行できます[ -t 1 ]。標準出力が端末の場合のみ成功します。


プロセスがttyであると開始されたアプリケーションをだます方法はありますか?
クリススミス

4
尋ねた答えでunix.stackexchange.com/questions/249723すでに、chris13523。ところで、コメントは実際には後続の質問の場所ではありません。
JdeBP

1
@ chris13524はJdeBPのリンクを参照してください。多くの場合、プログラムにカラーコードを強制的に出力させることもできます(更新された回答を参照)。
スティーブンキット

13

環境変数はありますか?

はい。これはTERM環境変数です。これは、決定プロセスの一部として使用されるものがいくつかあるためです。

すべてのプログラムが単一の決定フローチャートに同意するわけではないため、ここで一般化することは困難です。実際grep、M。Kittの回答で言及されているGNU は、予想外の結果を伴うやや異常な決定プロセスを使用する外れ値の良い例です。したがって、非常に一般的な用語では:

  • 標準出力は、によって決定される端末デバイスである必要がありますisatty()
  • プログラムは、termcap / terminfoデータベースで端末タイプのレコードを検索できる必要があります。
  • したがってそこになければならないことがルックアップするために端末タイプ。TERM環境変数が存在している必要があり、その値は、データベースレコードと一致する必要があります。
  • したがって、terminfo / termcapデータベースが必要です。サブシステムの一部の実装では、termcapデータベースの場所はTERMCAP環境変数を使用して指定できます。そのため、一部の実装では、2番目の環境変数があります。
  • termcap / terminfoレコードは、端末タイプが色をサポートしていることを示さなければなりません。max_colorsterminfoにはフィールドがあります。実際にカラー機能を持たない端末タイプには設定されていません。実際、すべてのカラー表示可能な端末タイプには、カラー機能がないことを示す名前の付いた、-mまたは-mono名前に追加された別のレコードがあるというterminfoの規則があります。
  • termcap / terminfoレコードは、プログラムが色を変更する方法を提供する必要があります。terminfoにはset_a_foregroundset_a_backgroundフィールドがあります。

単にチェックするよりも少し複雑ですisatty()。それが行われ、さらにいくつかのことによって複雑に:

  • 一部のアプリケーションは、isatty()チェックを無効にするコマンドラインオプションまたは構成フラグを追加します。これにより、プログラムは、出力として(色付け可能な)ターミナルがあると常にまたは決して想定しません。たとえば:
    • GNU lsには--colorコマンドラインオプションがあります。
    • BSDはlsCLICOLOR(その不在は意味ありませ)とCLICOLOR_FORCE(その存在は意味常に)環境変数、また、スポーツ-Gコマンドラインオプションを。
  • 一部のアプリケーションでは、termcap / terminfoを使用せず、の値に対する応答がハードワイヤードされていますTERM
  • すべての端末が、色を変更するために「ANSIエスケープシーケンス」と少し間違って名付けられたECMA-48またはISO 8613-6 SGRシーケンスを使用するわけではありません。実際、termcap / terminfoメカニズムは、正確な制御シーケンスの直接的な知識からアプリケーションを隔離するように設計されています。(さらに、誰もが ISO 8613-6 SGRシーケンスを使用しないという議論があります。これは、RGBカラーSGRシーケンスの区切り文字としてセミコロンを使用するバグ誰もが同意しているためです。標準では実際にコロンが指定されています。)

前述のように、GNUはgrep実際にこれらの追加の複雑さのいくつかを示します。termcap / terminfoを参照せず、制御シーケンスを出力するように配線し、TERM環境変数への応答を配線します。

それののLinux / Unixのポートは、このコードを持っている場合にのみ、色化を可能にする、TERM環境変数が存在し、その値は、ハードワイヤードの名前と一致していませんdumb

int
should_colorize(void)
{
  char const * t = getenv( "TERM");
  return t && strcmp(t、 "dumb")!= 0;
}

ですから、たとえあなたTERMxterm-monoであるとしても、GNU grepは他のプログラムvimはそうではないにしても、色を出すことを決定します。

それのWin32ポートにはこのコードがあり、TERM環境変数存在しない場合、または存在し、その値が配線された名前と一致しない場合に色付けを可能にしますdumb

int
should_colorize(void)
{
  char const * t = getenv( "TERM");
  戻ります!(t && strcmp(t、 "dumb")== 0);
}

GNU grepの色に関する問題

GNU grepの色付けは実際には悪名高いです。実際には端末出力を構築する適切な仕事をしていませんが、出力のさまざまなポイントでいくつかのハードワイヤード制御シーケンスを非難するだけで、それが十分であることを望んでいないため、特定の状況で実際に誤った出力を表示します。

これらの状況は、端末の右端にある何かを色付けする必要がある場合です。端末出力を適切に行うプログラムは、自動右マージンを考慮する必要があります。 また、端末のかもしれないが、それらを持っていないというわずかな可能性に(VIZ auto_right_marginのterminfo内のフィールド)、自動右マージンを持っていない端末の動作は、多くの場合の12月VTの先例次の行の折り返しを保留します。GNU grepはこれを考慮せず、素直な行の折り返しを単純に期待しており、その色付き出力は間違っています。

カラー出力は単純なものではありません。

参考文献


2
私が理解しているように、OPは出力がリダイレクトされるときの動作の変更について尋ねています。$TERMそれを説明しません。(あなたの答えは一般的に興味深いですが、私はそれが質問に対処するとは思わない...)
スティーブンキット

とても興味深い。私は、プログラムがどのように端末機能が何であるかを発見する(または単に「決定する」)ことについて、このような概要を数か月間求めていました。これはまた、なぜこのような概要を見つけるのが難しいのかという洞察を与えます-各プログラムがそれをわずかに異なって行うように見えるので。
the_velour_fog

保留中の行の折り返し即時の行の折り返しの意味の説明と、その違いを示す例があれば便利です。
mosvy

0

expectパッケージのunbufferコマンドは、最初のプログラムからの出力と2番目のプログラムへの入力を分離します。

次のように使用します。

unbuffer myshellscript.sh | grep value

ansibleと自作のcteeスクリプトで常に使用するので、ログファイルを通常の(色付けされていない)出力のままにして、ターミナルで色出力を確認できます。

unbuffer ansible-playbook myplaybook.yml | ctee /var/log/ansible/run-$( date "+%F" ).log
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.