任意の文字列を含む行を色付けして出力するシェルコマンド


コマンドの出力内の任意の文字列を含む行を色付けしたいと思ったので、シェルコマンドでやってみた。

コマンド

file.txt内の"hoge"を含む行を赤文字にして出力するシェルコマンドの例。

$ cat file.txt | sed -r "s/(.*hoge.*)/\x1b[38;5;9m\1\x1b[0m/g"

解説

色付け処理

\x1b[38;5;9mはこれ以後の文字列を赤色に変更するための特殊な文字列であり、\x1b[0mはこれ以後の文字列をデフォルトの色に戻す。

"\x1b[38;5;9m文字列\x1b[0m"

これを出力すると、赤色の「文字列」が出力される。

上記の文字列のうち\xb1はESCの16進数表記である。38のところを48にすると文字色ではなく、背景色を変更することができ、9を変更することで色を変更することができる。色のコードは0~255まで存在する。

この色付け処理について詳しく知りたい場合は、 https://misc.flogisoft.com/bash/tip_colors_and_formatting を参考にすると良い。

置換処理

sedコマンドは正規表現を用いて置換を行うシェルコマンドで、"s/置換前文字列/置換後文字列/g"と引数に渡すことで、置換を行う。

sed "s/置換前文字列/置換後文字列/g"

.は改行を除く任意の一文字を意味し、*は直前の文字を任意数繰り返すことを意味する。

なので、.*hoge.*は"hoge"の前後が任意の文字列となる文字列を示すこととなる。

()は置換後文字列内に存在する\1で参照するために必要。置換前文字列で()内の文字列にヒットした結果が\1に展開される。(このためには拡張正規表現を使用する必要がある)

すなわち、以下のシェルコマンドは何も置換が行われないことになる。

sed -r "s/(.*hoge.*)/\1/g"

これを用いると、任意の文字列の前後に、任意の文字列を挿入することができる。
例として、以下のシェルコマンドで置換前文字列の前後に"A"、"B"を挿入することができる。

sed -r "s/(.*hoge.*)/A\1B/g"

組み合わせると

よって、以上の機能を組み合わせることで、"hoge"を含む行の前後に色付け用の特殊な文字列を挿入することができ、文字色を赤色に変更することができる。

sed -r "s/(.*hoge.*)/\x1b[38;5;9m\1\x1b[0m/g"

おまけ

色付けされた出力をlessコマンドにただパイプしても色がつかない。
この場合は、オプションの-Rをつけると良い。

$ cat file.txt | sed -r "s/(.*hoge.*)/\x1b[38;5;9m\1\x1b[0m/g" | less -R

さいごに

パイプで入力を渡されたときのみ機能する、任意文字列を含む行のみを色付けして出力するための単純なシェルスクリプトを載せておく。第一引数で検索する文字列を指定し、第二引数で文字色を指定することができる。

#!/bin/bash

if [ -p /dev/stdin ]
then
  if [ "$1" = "" ]
  then
    cat -
    exit 0
  fi

  WORD=$1
  # Default color is red
  COLOR=9
  # Check the second argument. Is it number and 0 <= $2 <= 255?
  expr "$2" + 1 > /dev/null 2>&1
  if [ "$?" -lt "2" ] && [ "$2" -gt "-1" ] && [ "$2" -lt "256" ]
  then
    COLOR=$2
  fi
  cat - | sed -r "s/(.*${WORD}.*)/\x1b[38;5;${COLOR}m\1\x1b[0m/g"
fi

追記

以下の記事で、色付けするシェルコマンドをGo言語でコマンドラインツールとして作成してみた。
「任意の文字列や行をハイライトするツールをGo言語で作成してみた」