stderr
私は赤以外の色で印刷されるように端末を設定したいと思いますstdout
。これにより、2つを簡単に区別できます。
で設定する方法はありますか.bashrc
?そうでなければこれは可能ですか?
ノート:この問題はマージされました。その他必要stderr
、stdout
ユーザー入力をエコーします。から出力予定3色。答えは2つの質問のうちの1つです。
答え1
確認するstderred
。端末に送信されたすべての出力に色を割り当てるために通話を接続するためにLD_PRELOAD
使用されます。 (デフォルトは赤です。)libc
write()
stderr
答え2
これはより難しいバージョンです。画面にstderrのみを表示し、stdoutとstderrをファイルに書き込みます。。
端末で実行されるアプリケーションは、単一チャネルを使用して通信します。アプリケーションには stdout と stderr という 2 つの出力ポートがありますが、どちらも同じチャネルに接続されています。
チャンネルの1つを別のチャンネルに接続し、そのチャンネルに色を追加して2つのチャンネルをマージできますが、これは2つの問題を引き起こします。
- マージされた出力は、リダイレクトがない場合とまったく同じ順序ではない可能性があります。これは、チャンネルの1つに追加された処理に(少し)時間がかかるため、カラーチャンネルが遅れる可能性があるためです。バッファリングがあると、障害がさらに悪化します。
- 端末は、色の変更エスケープシーケンスを使用して表示色を決定します(たとえば、
␛[31m
「赤の前景に切り替える」という意味)。つまり、stderrの一部の出力が表示されているときにstdoutの一部の出力が到着すると、出力の色が正しくありません。 (もっと悪いのは、エスケープシーケンスの途中にチャンネルスイッチがあるとゴミが表示されることです。)
原則として、2つのptys1を同時に受信し(つまり、別のチャンネルの出力を処理している間に1つのチャンネルの入力を受け付けず)、適切な色変更コマンドを使用してすぐに端末に出力するプログラムを書くことができます。端末と対話するプログラムを実行できなくなります。この方法の実装についてはわかりません。
write
別の可能な方法は、ロードされたライブラリからシステムコールを呼び出すすべてのlibc関数をリンクして、プログラムが正しい色の変更順序を出力するようにすることです。LD_PRELOAD
。バラよりシギルの答え既存の実装の場合、またはStefan Chazerasの答え混合方法の使用strace
。
実際には、該当する場合は、stderrをstdoutにリダイレクトし、パターンベースのシェーダにパイピングすることをお勧めします。あやおまたはマルチテールまたは、次の特殊目的シェーダカラーgccまたはカラー生産。
擬似端末1個。バッファリングのため、パイプラインは機能しません。ソースはバッファに書き込むことができ、これはシェーダとの同期を中断します。
答え3
半分の場合、端末ドライバ(ローカルエコーを含む)によって出力されるため、ユーザー入力色を指定することは困難です。したがって、この場合、その端末で実行されているアプリケーションは、テキストを入力して出力を変更しようとすると、ユーザーがどこにいるかを知ることができません。それに応じて色を指定します。 (カーネル内の)疑似ターミナルドライバだけが特定のキーを押すといくつかの文字を送信することがわかります(xtermなどのターミナルエミュレータ)できません。または擬似端子スレーブへのアプリケーション出力)。
その後、端末ドライバにエコーしないように指示する別のモードがありますが、今回はアプリケーションが何かを出力します。ユーザー入力をエコーする代わりに、アプリケーション(gdb、bashなどのreadlineを使用するアプリケーション)はそれをstdoutまたはstderrに送信することができます。
次に、アプリケーションの標準出力と標準エラーを区別するためのいくつかの方法があります。
これらの多くには、stdoutコマンドとstderrコマンドをパイプにリダイレクトし、これらのパイプを読み取って色を指定するアプリケーションが含まれます。これには2つの問題があります。
- stdoutが端末(パイプなど)になっていない場合、多くのアプリケーションは出力バッファリングを開始するように動作を調整する傾向があります。つまり、出力は大きな塊として表示されます。
- 両方のパイプを処理する同じプロセスであっても、アプリケーションがstdoutとstderrに書き込んだテキストの順序が維持されるという保証はありません。両方) アプリケーションが stdout と stderr で作成したテキストの順序を保持して読み込みを開始するかどうか。
別のアプローチは、標準出力と標準入力の両方に色を付けるようにアプリケーションを変更することです。これはしばしば不可能または非実用的です。
次に、(動的にリンクされたアプリケーションの場合)トリックは、次のものを$LD_PRELOAD
使用してハイジャックすることができます。シギルの答え)アプリケーションが何かを出力するために呼び出し、stderrまたはstdoutに何かを出力するかどうかに応じて前景色を設定するコードを含む出力関数。しかし、これは、Cライブラリとアプリケーションから直接呼び出されるシステムコールを実行する他のライブラリwrite(2)
で可能なすべての機能をハイジャックすると、最終的にstdoutまたはstderr(printf、puts、perror ....)に何かを書く可能性があることを意味します。 、これにより動作が変わる可能性があります。
strace
別のアプローチは、システムコールが呼び出されるたびにPTRACEトリックを使用して、ファイル記述子がファイル記述子1または2にあるかどうかに応じて出力色を設定することです。gdb
write(2)
write(2)
しかし、これはかなり大きな問題です。
私が使った1つのトリックは、LD_PRELOADを使用して自分自身をハイジャックし(すべてのシステムコールの前に自分自身を接続する汚れた作業を実行します)、fd 1または2が検出されたかどうかに依存するstrace
ように指示することです。write(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 -qqf -a0 -s0 -o /dev/null \
-e write -e status=successful -P "$(tty)" \
env -u LD_PRELOAD some-cmd
some-cmd
で置き換えると、bash
bashプロンプトと入力した内容は赤(stderr)で表示され、zsh
代替項目は黒で表示されます(zshがプロンプトとエコーを表示するためにstderrを新しいfdにコピーするため)。
予期しないアプリケーション(色を使用するアプリケーションなど)でも驚くほどうまく機能しているようです。
strace
シェーディングモードは、端末とみなされたstderrに出力されます。を使用すると、-P "$(tty)"
stdout / stderrがリダイレクトされている場合など、端末に送信されていない書き込みに対してこれを回避できます。
このソリューションには次の制限があります。
- 本質的なもの:パフォーマンスの問題、または
strace
その中で他のPTRACEコマンドを実行できない、setuid / setgidの問題strace
gdb
- 色は
write
各個々のプロセスのstdout / stderrに基づいています。たとえば、sh -c 'echo error >&2'
次のように出力されるのでerror
緑になります。echo
それstdout(shはshのstderrにリダイレクトされますが、straceが見るすべてはaですwrite(1, "error\n", 6)
)。
2021年10月に修正されました。このラッパーは、strace 5.10、glibc 2.32、gcc 10.30.0を使用する不安定なDebianでは動作しなくなりました。ラップする必要がある関数__vfprintf_chk
と検索する必要がある型が変更されたためです。ラッパーを次に変更する必要があります。
#define _GNU_SOURCE
#include <dlfcn.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
int __vfprintf_chk(FILE *outf, int x, const char *fmt, va_list ap)
{
static int (*orig_vfprintf) (FILE*, int, 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*, int, const char *, va_list))
dlsym (RTLD_NEXT, "__vfprintf_chk");
}
if (strcmp(fmt, "%d") == 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, "= %lu") == 0) {
if (c) write(2, "\e[m", 3);
c = 0;
}
return orig_vfprintf(outf, x, fmt, ap_orig);
}
このアプローチは、文書化されていない不安定なAPI(実際のAPIではない)を使用するため、わずかに脆弱であることを示しています。
答え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
}