プログラムはどのようにして出力に色をつけるかどうかを決定するのでしょうか?

プログラムはどのようにして出力に色をつけるかどうかを決定するのでしょうか?

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

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

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

答え1

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

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

答え2

環境変数はありますか?

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

ここで一般化するのは困難です。すべてのプログラムが単一の決定フローチャートに同意するわけではないからです。実際、grepM. Kitt の回答で言及されている GNU は、予想外の結果をもたらすやや変わった決定プロセスを使用する外れ値の良い例です。したがって、非常に一般的な言葉で言えば、次のようになります。

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

単にチェックするよりも少し複雑ですisatty()さらに遠くいくつかの要因によって複雑化しています:

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

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

Linux/Unix版にはこのコードがあるTERM環境変数が存在し、その値がハードワイヤード名と一致しない場合にのみ色付けを有効にしますdumb

整数
色付けする必要がある (void)
{
  char const *t = getenv ("TERM");
  t を返します && strcmp (t, "dumb") != 0;
}

したがって、 の場合でもTERM、などの他のプログラムがそうでなくてもxterm-mono、GNU は色を出力することを決定します。grepvim

Win32ポートにはこのコードがあります環境TERM変数ではない存在する場合、または存在していてもその値がハードワイヤード名と一致しない場合dumb:

整数
色付けする必要がある (void)
{
  char const *t = getenv ("TERM");
  戻り値 ! (t && strcmp (t, "dumb") == 0);
}

GNUgrepの色に関する問題

GNUgrepのカラー化は、実のところ悪名高いものです。端末出力を適切に構築するのではなく、出力のさまざまなポイントでいくつかのハードワイヤード制御シーケンスを単に挿入して、それで十分だろうという無駄な期待を抱くだけなので、特定の状況では実際に誤った出力が表示されます。

このような状況では、端末の右端にあるものを色付けする必要があります。端末出力を適切に行うプログラムは、自動右端マージンを考慮する必要があります。 加えて端末が自動右マージンを持たない可能性(auto_right_marginterminfoのフィールド)はわずかですが、自動右マージンを持つ端末の動作は、DEC VTの先例に従うことが多いです。保留中の行折り返しGNUはgrepこれを考慮しておらず、単純に期待している。即時の行折り返し、色付きの出力は間違っています。

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

参考文献

答え3

unbufferからの命令期待するパッケージは、最初のプログラムからの出力と 2 番目のプログラムへの入力を切り離します。

次のように使用します:

unbuffer myshellscript.sh | grep value

私はいつもAnsibleと自作のcteeスクリプトを実行すると、ターミナル上でカラー出力を確認でき、ログ ファイルには通常の (カラー化されていない) 出力が残ります。

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

関連情報