jqとls --colorの不思議とisatty
コマンド出力に色が着く
ls --color
などを実行すると文字に色が着く。こういうふうに。
これはjq
も同じで普通にコマンドを実行すると色が着く。
pythonで
これらはansi-color-code付きで出力されているので色が着く。pythonでやってみるとこういう感じ。
print("[01;34mhello[0m world")
リダイレクトしたりパイプでつなげると色がなくなる。
ls --color
はリダイレクトしたりパイプでつなげても色が残るのに対して、jq
はリダイレクトすると色が消える。例えばjqの出力をcatに渡してみる。色が消える。
この振る舞いはどうやっているんだろう?というのが今回の話。
isatty
正体はisattyでこれは渡されたファイルディスクリプタがターミナルから開かれているかどうか調べてくれるもの。実際jqなどのコードを覗いてみたら使われている箇所があった。
おもむろにmanを見てみるとそれっぽいことが書かれている。
NAME isatty - test whether a file descriptor refers to a terminal SYNOPSIS #include <unistd.h> int isatty(int fd); DESCRIPTION The isatty() function tests whether fd is an open file descriptor referring to a terminal. RETURN VALUE isatty() returns 1 if fd is an open file descriptor referring to a terminal; otherwise 0 is returned, and errno is set to indicate the error.
pythonでもisatty
pythonにもisattyは存在していて、os.isatty()を使っても良いけれど。
ファイル記述子 fd がオープンされていて、 tty (のような) デバイスに接続されている場合、 True を返します。そうでない場合は False を返します。
標準出力などがisatty()
メソッドを持っているのでこれを使えば良い。
実際に使って確かめてみる。
$ python -c 'import sys; print(sys.stdout.isatty())' True $ python -c 'import sys; print(sys.stdout.isatty())' | cat False
良さそう。
pythonでもjqの振る舞いを模倣する
あとはpythonでもjqの振る舞いを模倣するにはどうするかという話。isattyがTrueだったら色を付けて出力すれば良い。
import sys if sys.stdout.isatty(): COLOR_ON = "[01;34m" COLOR_OFF = "[01;34m" else: COLOR_ON = "" COLOR_OFF = "" print(f"{COLOR_ON}hello{COLOR_OFF} world")
今度は色が付いたりつかなかったりしてくれる。