web-dev-qa-db-ja.com

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

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

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

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

17
Chris Smith

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

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

25
Stephen Kitt

環境変数はありますか?

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

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

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

単にisatty()をチェックするよりも少し複雑です。 さらにいくつかのことで複雑にされます:

  • 一部のアプリケーションは、isatty()チェックをオーバーライドするコマンドラインオプションまたは構成フラグを追加し、プログラムalwaysまたはneverは、出力として(着色可能な)端末があると想定します。たとえば:
    • GNU lsには_--color_コマンドラインオプションがあります。
    • BSD lsは、CLICOLOR(不在の意味never)と_CLICOLOR_FORCE_(その存在の意味)を常に調べます)環境変数、および_-G_コマンドラインオプションも使用できます。
  • 一部のアプリケーションは、termcap/terminfoを使用せず、TERMの値に対する応答をハードワイヤードしています。
  • すべての端末が、色を変更するために、ECMA-48またはISO 8613-6 SGRシーケンスを使用しているわけではありません。 termcap/terminfoメカニズムは、実際には、正確な制御シーケンスの直接的な知識からアプリケーションを隔離するように設計されています。 (さらに、no-oneはISO 8613-6 SGRシーケンスを使用するという引数があります。なぜなら、everyone 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; 
}

したがって、TERMが_xterm-mono_である場合でも、GNU grepは、vimなどの他のプログラムが色を放出しないことを決定します。

Win32ポートにはこのコードがあります 。これは、TERM環境変数が存在しない場合または存在する場合に色付けを有効にします。そしてその値は、ハードワイヤード名dumbと一致しません:

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

GNU grepの色に関する問題

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

これらの状況では、端末の右端にあるものを色分けする必要があります。端末出力を適切に行うプログラムは、自動右マージンを考慮する必要があります。 これに加えて端末がそれらを持たない可能性があります(terminfoの_auto_right_margin_フィールドを参照)。自動右マージンは、多くの場合、DEC VTの前例の保留行の折り返しに従います。 GNU grepはこれを考慮しておらず、即時の行の折り返しを期待していないため、色付きの出力が正しくありません。

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

参考文献

13
JdeBP

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

次のように使用します。

unbuffer myshellscript.sh | grep value

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

unbuffer ansible-playbook myplaybook.yml | ctee /var/log/ansible/run-$( date "+%F" ).log
0
bgStack15