STDERRとSTDOUTを異なる色で印刷するようにシェルを構成できますか?


63

端末を設定したいのでstderr、とは異なる色で印刷されstdoutます。たぶん赤。これにより、2つを区別しやすくなります。

これを設定する方法はあります.bashrcか?そうでない場合、これも可能ですか?


:この質問はと合併したを求めているstderrstdout とユーザー入力のエコーで出力されるように3色展開。回答はどちらの質問にも対応している可能性があります。


1
スタックオーバーフロー上の同じ質問:stackoverflow.com/questions/6841143/...
ステファン・ヒメネス

以来、興味深い質問+の答えは、しかし、赤はあまりIMO際立っ標準エラー出力にエラーのためだけではなくです
krookedking

回答:


32

これは、画面にstderrのみ表示するハードバージョンですが、stdoutとstderrの両方をfileに書き込みます

ターミナルで実行されているアプリケーションは、単一のチャネルを使用して通信します。アプリケーションには、stdoutとstderrの2つの出力ポートがありますが、どちらも同じチャネルに接続されています。

それらの1つを別のチャネルに接続し、そのチャネルに色を追加し、2つのチャネルをマージできますが、これにより2つの問題が発生します。

  • マージされた出力は、リダイレクトがなかった場合とまったく同じ順序ではない場合があります。これは、チャネルの1つで追加された処理に(少し)時間がかかるため、色付きのチャネルが遅延する可能性があるためです。バッファリングが行われると、障害は悪化します。
  • 端末は、色を変更するエスケープシーケンスを使用して表示色を決定します。たとえば、␛[31m「赤い前景に切り替える」という意味です。これは、stderrの出力が表示されているときにstdout宛ての出力が到着した場合、出力の色が正しくないことを意味します。(さらに悪いことに、エスケープシーケンスの途中でチャネルの切り替えがある場合は、ゴミが表示されます。)

原則として、2つのptys¹で同期的にリッスンするプログラムを作成し(つまり、1つのチャネルで入力を受け入れず、他のチャネルで出力を処理します)、適切な色変更命令ですぐに端末に出力できます。端末と対話するプログラムを実行する機能が失われます。このメソッドの実装については知りません。

別の可能なアプローチはwrite、でロードされたライブラリでシステムコールを呼び出すすべてのlibc関数をフックすることにより、プログラムに適切な色変更シーケンスを出力させることLD_PRELOADです。既存の実装に対するシックルの回答、またはを活用する混合アプローチに対するステファンシャゼラスの回答を参照してくださいstrace

それが該当するかどう実際には、私のようなパターンベースのカラー表示機能にstdoutとstderrの配管にリダイレクト示唆colortailまたはmultitail、またはのような専用の色付け機能(colorizers)colorgccまたはcolormake

¹ 擬似端末。バッファリングのため、パイプは機能しません。ソースがバッファに書き込むことができ、カラーライザとの同期が崩れます。


1
stderrストリームを色付けするために端末プログラムにパッチを当てることは難しくないかもしれません。誰かがubuntuのブレインストーミングでこのようなことを提案しました。
直観

@intuited:これを使用したいすべてのターミナルエミュレータをパスする必要があります。使用LD_PRELOAD傍受するトリックをwriteIMO、最も適切であると思われる(。しかし、その後、再び、特定の* nixの味の違いがあるかもしれません)の呼び出しを
アレックス

少なくともLinux上で、インターセプトするwriteほとんどのアプリケーションは直接呼び出さないとして単独では機能しないだろうが、いくつかの共有ライブラリ(等からの別の関数printfの元を呼ぶだろう)write
ステファンChazelas

@StephaneChazelas writesyscallラッパーをフックすることを考えていました。Glibcの他の関数にインライン化されていますか?
ジル「SO-悪であるのをやめる」

1
stderredプロジェクトは、フックのインプリメンテーションであるように思わwrite経由してLD_PRELOAD、あなたが説明するように。
ドリュー

36

チェックアウトしstderredます。これはの呼び出しLD_PRELOADにフックして、端末へのすべての出力を色付けするために使用します。(デフォルトでは赤。)libcwrite()stderr


8
素敵な、そのライブラリは素晴らしいです。本当の質問は、オペレーティングシステム/端末にこのプレインストールが付属していないのはなぜですか?;)
ナフトゥリケイ

5
あなたが著者だと思いますが、そうですか?その場合、所属を開示する必要があります。
ドミトリーグリゴリエフ

15

ユーザー入力の色付けは難しい場合があります。半分の場合、ターミナルドライバーによって出力されるため(ローカルエコーを使用)、その場合、そのターミナルで実行中のアプリケーションは、ユーザーがテキストを入力し、それに応じて出力色を変更するタイミングを認識できません。(カーネル内の)疑似端末ドライバーのみが知っています(端末エミュレーター(xtermなど)はキーを押すといくつかの文字を送信し、端末ドライバーはエコーのためにいくつかの文字を送り返しますが、xtermはそれらがローカルエコーまたはアプリケーションが擬似端末のスレーブ側に出力したもの)。

そして、ターミナルドライバーにエコーしないように指示する別のモードがありますが、今回はアプリケーションが何かを出力します。アプリケーション(gdb、bashなどのreadlineを使用するアプリケーションなど)は、stdoutまたはstderrで送信する場合があり、ユーザー入力をエコーバックする以外の目的で出力するものと区別するのが困難になります。

次に、アプリケーションの標準出力と標準エラーを区別するために、いくつかのアプローチがあります。

それらの多くは、コマンドstdoutおよびstderrをパイプにリダイレクトし、それらのパイプがアプリケーションによって読み取られて色付けされます。これには2つの問題があります。

  • stdoutがターミナルではなく(パイプのように)なると、多くのアプリケーションは動作を適応させて出力のバッファリングを開始する傾向があります。これは、出力が大きなチャンクで表示されることを意味します。
  • 2つのパイプを処理するのが同じプロセスであっても、読み取りプロセスが知ることができないため(両方から読み取るものがある場合)、stdoutとstderrでアプリケーションによって書き込まれたテキストの順序が保持される保証はありません「stdout」パイプまたは「stderr」パイプから読み取りを開始するかどうか。

別のアプローチは、stdoutとstdinを色付けするようにアプリケーションを変更することです。多くの場合、これは不可能または現実的ではありません。

次に、(動的にリンクされたアプリケーションの)トリックは、何かを出力するためにアプリケーションによって呼び出される出力関数をハイジャックする(sickillの答えの$LD_PRELOADように使用する)ことができ、何かを出力するかどうかに基づいて前景色を設定するコードを含めるstderrまたはstdoutで。ただし、Cライブラリや、stdoutまたはstderr(printf、puts、perror ...)に何かを書き込む可能性のあるアプリケーションによって直接呼び出されるsyscall を実行する他のライブラリからすべての可能な関数をハイジャックすることを意味します、その動作を変更する場合があります。write(2)

別のアプローチは、としてPTRACEのトリックを使用することができstraceたりgdb、私たち自身を毎回フックするために行うwrite(2)システムコールが呼び出されたのかどうかに基づいて、出力カラー設定write(2)ファイルディスクリプタ1または2であるが。

ただし、それは非常に大きなことです。

私がちょうど遊んでいたトリックstraceは、write(2)fd 1または2。

見てからstraceソースコードを、我々はそれが出力のすべてを介して行われていることがわかりますvfprintf機能。必要なのは、その機能をハイジャックすることだけです。

LD_PRELOADラッパーは次のようになります。

#define _GNU_SOURCE
#include <dlfcn.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>

int vfprintf(FILE *outf, const char *fmt, va_list ap)
{
  static int (*orig_vfprintf) (FILE*, const char *, va_list) = 0;
  static int c = 0;
  va_list ap_orig;
  va_copy(ap_orig, ap);
  if (!orig_vfprintf) {
    orig_vfprintf = (int (*) (FILE*, const char *, va_list))
      dlsym (RTLD_NEXT, "vfprintf");
  }

  if (strcmp(fmt, "%ld, ") == 0) {
    int fd = va_arg(ap, long);
    switch (fd) {
    case 2:
      write(2, "\e[31m", 5);
      c = 1;
      break;
    case 1:
      write(2, "\e[32m", 5);
      c = 1;
      break;
    }
  } else if (strcmp(fmt, ") ") == 0) {
    if (c) write(2, "\e[m", 3);
    c = 0;
  }
  return orig_vfprintf(outf, fmt, ap_orig);
}

次に、次のようにコンパイルします。

cc -Wall -fpic -shared -o wrap.so wrap.c -ldl

そしてそれを次のように使用します:

LD_PRELOAD=/path/to/wrap.so strace -qfo /dev/null -e write -s 0 env -u LD_PRELOAD some-cmd

に置き換えるsome-cmdbash、bashプロンプトと入力内容が赤(stderr)zshで表示され、黒で表示されます(zshはstderrを新しいfdにコピーしてプロンプトとエコーを表示するため)。

期待しないアプリケーション(色を使用するアプリケーションなど)でも驚くほどうまく機能するようです。

カラーモードはstrace、端末であると想定されるの標準エラー出力に出力されます。アプリケーションがそのstdoutまたはstderrをリダイレクトする場合、ハイジャックされたstraceはターミナルでカラーリングエスケープシーケンスを書き続けます。

このソリューションには制限があります。

  • 固有のものstrace:パフォーマンスの問題、straceまたはそのような他のPTRACEコマンドを実行できないgdb、またはsetuid / setgidの問題
  • write個々のプロセスの標準出力/標準エラーのsに基づいた色付けです。だから、例えば、でsh -c 'echo error >&2'errorあるため、緑だろうechoに出力するその標準出力(SHのstderrにリダイレクトSH、しかし、すべてのstraceのがAで見ていますwrite(1, "error\n", 6))。そして、でsh -c 'seq 1000000 | wc'seqロットまたはないwriteにSをその標準出力なので、ラッパーは端末にエスケープシーケンス(見えない)の多くをoutputingなってしまいます。

いいね 重複した質問に関する既存のラッパーの提案がありました。質問をマージするためのフラグを立てて、そこで回答が表示されるようにしました。
ジル「SO-悪であるのをやめる」

たぶんvim構文のハイライトを微調整する?strace $CMD | vim -c ':set syntax=strace' -
パブロA

4

これは、私がしばらく前に行った概念実証です。

zshでのみ機能します。

# make standard error red
rederr()
{
    while read -r line
    do
        setcolor $errorcolor
        echo "$line"
        setcolor normal
    done
}

errorcolor=red

errfifo=${TMPDIR:-/tmp}/errfifo.$$
mkfifo $errfifo
# to silence the line telling us what job number the background job is
exec 2>/dev/null
rederr <$errfifo&
errpid=$!
disown %+
exec 2>$errfifo

また、setcolorという関数があることを前提としています。

簡易版:

setcolor()
{
    case "$1" in
    red)
        tput setaf 1
        ;;
    normal)
        tput sgr0
        ;;
    esac
}

これを行うには、はるかに簡単な方法がありますexec 2> >(rederr)。どちらのバージョンにも、回答で述べたように、行の並べ替えと出力の破損のリスク(特に長い行)の問題があります。
ジル「SO-悪であるのをやめる」

私はそれを試しましたが、うまくいきませんでした。
ミケル

seterr関数ではなく、スタンドアロンスクリプトである必要があります。
ジル 'SO-悪であるのをやめる'

4

一度に1つのコマンドでこれを実行するMike SchiraldiのHiliteを参照してください。私自身のガッシュはセッション全体でこれを行いますが、あなたが望まないかもしれない他の多くの機能/特異性も持っています。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.