grep

curl の出力が grep でうまく色付けできない時の対処

grep でパターンマッチする部分に色づけしたいが、うまくいかなかったので調べた際のメモ。

結論から言うと CR が問題のトリガーになっていたので、curl の出力に限ったことではなかった。

症状

curl の結果を出力しつつ Server ヘッダに注目して見たいので、Server の部分に色を付けたい。

$ curl -s -D - google.co.jp | grep --color=auto -E '^|Server:'

_curl_01.png

上記はうまくいく。

色付けを行末までにしようと、正規表現に .* を追加する。

$ curl -s -D - google.co.jp | grep --color=auto -E '^|Server:.*'

_curl_02.png

色付けされると思いきや、見えなくなってしまった。

調査

hexdump で見てみる。

$ curl -s -D - google.co.jp | grep --color=auto -E '^|Server:.*' | hexdump -C

_curl_03.png

Server: 行は存在するが、エスケープシーケンスが見えていない。
grep --color=auto の場合は出力先が制御端末でないと自動的に色付けを解除するようだ。

--color=always で見てみる。

$ curl -s -D - google.co.jp | grep --color=always -E '^|Server:.*' | hexdump -C

_curl_04.png

Server: もエスケープシーケンスもあるようなので、表示する際に何かうまくいっていないようだ。

ちょっと見づらいので、strace の write(2) の方を確認する。

## strace の出力先はファイルにする
## 端末に出そうとするとエスケープシーケンスが効いてしまうので注意
$ curl -s -D - google.co.jp | strace -y -o trace.txt -e write grep --color=auto -E '^|Server:.*'
$ grep "Server:" trace.txt
write(1</dev/pts/2>, "\33[01;31m\33[KServer: gws\r\33[m\33[K\n", 30) = 30

<ESC>[K は Erase in Line で、現在位置から行末までを消去するらしい。
Server: gws は色づけされているが、Carriage Return で行頭に飛んでから Erase in Line で消去されて改行されるので、表示されなくなっていた。

回避策

というわけで、\r を sed などで除去したうえで grep にかければ解決する。

$ curl -s -D - google.co.jp | sed 's/\r//g' | grep --color=auto -E '^|Server:.*'

_curl_05.png

また、ググると grep の環境変数での解決策も見つかる。

https://unix.stackexchange.com/questions/350352/grep-color-auto-breaks-when-m-is-inside-colored-match

GREP_COLORS="ne" とすることで、Erase in Line を追加しないようにしている。

$ curl -s -D - google.co.jp | GREP_COLORS="ne" grep --color=auto -E '^|Server:.*'

_curl_06.png