LoginSignup
6
4

More than 5 years have passed since last update.

tail, grepを使って特定キーワードを含む行以下を表示する

Last updated at Posted at 2018-12-07

この記事はSRA Advent Calendar 2018 の6日目の記事です。
カレンダーが空いていたので埋め草です。

やりたいこと

ログとかを調査していて、「あー、この日付以降の行だけ見たいんだけどなあ」ということがありませんか。
わたしはよくあります。ログがつねに適切な量でローテーションされていたらこんな苦労ないのに。。。

こうすればできる


tail -n +$(grep -n "キーワード" ファイル名 |head -1| sed 's/:.*$//') ファイル名

適当に解説

まず、バッククォートの中身からです。
grep -n "キーワード" ファイル名 でもって、対象ファイルからそのキーワードがある行を行番号付きでgrepしています。
ex.


$  grep -n "Dec 06" catalina.out
204325:Dec 06, 2018 10:44:52 AM org.apache.catalina.startup.VersionLoggerListener log
204327:Dec 06, 2018 10:44:52 AM org.apache.catalina.startup.VersionLoggerListener log
204329:Dec 06, 2018 10:44:52 AM org.apache.catalina.startup.VersionLoggerListener log
:

次にパイプで |head -1 が続くので、特定キーワードを含む行の先頭だけを取り出しています。
さらにパイプで|sed 's/:.*$//' が続いているので、特定キーワードを含む行がでてきた行番号だけ取り出せます。

$ grep -n "Dec 06" catalina.out  |head -1
204325:Dec 06, 2018 10:44:52 AM org.apache.catalina.startup.VersionLoggerListener log
$ grep -n "Dec 06" catalina.out  |head -1|sed 's/:.*$//'
204325

で、この行数が tail -n +行数 ファイル名 に入ってくるので、特定キーワードを含む行以降だけ出力できるわけです。


$ tail -n +$(grep -n "Dec 06" catalina.out  |head -1|sed 's/:.*$//') catalina.out  | head
Dec 06, 2018 10:44:52 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server version:        Apache Tomcat/7.0.86
Dec 06, 2018 10:44:52 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server built:          Apr 9 2018 20:16:54 UTC
Dec 06, 2018 10:44:52 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server number:         7.0.86.0
:

しかし、毎回、これを打つのも面倒です。ファイル名二回も打たなくちゃいけないし。
よし、functionにしよう。

.bashrc にfunctionとして登録する

これを.bashrcの最後にいれました。


function greptail() {
  if [ -p /dev/stdin  ]; then
    tmpf=$(mktemp "/tmp/greptail.tmp.XXXXXX")
    cat > $tmpf
    tail -n +$(grep -n "$1" $tmpf|head -1 | sed 's/:.*$//') $tmpf
    rm -f $tmpf
  else
    tail -n +$(grep -n "$1" $2 |head -1| sed 's/:.*$//') $2
  fi
}

使い方

ファイル名を指定する場合はこう。greptailの第一引数にキーワード、第二引数にファイル名を与えます。


$ greptail "Dec 06" catalina.out
Dec 06, 2018 10:44:52 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server version:        Apache Tomcat/7.0.86
Dec 06, 2018 10:44:52 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server built:          Apr 9 2018 20:16:54 UTC
:

標準入力を利用することも一応できます。

$ cat catalina.out |greptail "Dec 06" 
Dec 06, 2018 10:44:52 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server version:        Apache Tomcat/7.0.86
:

キーワードがgrepで存在しなかったりすると、こうでますが、ご愛嬌。


$ greptail "naiyo-" catalina.out
tail: invalid number of lines: ‘+’

軽く解説(いいわけ)

if文の前半は後回しにしておいて、else節の中身は先程のコマンドのものとまるきり同じです。

    tail -n +$(grep -n "$1" $2 |head -1| sed 's/:.*$//') $2

grepに渡す$1の周りをダブルクォートで囲い忘れると途端に動かなくなる。

さて、後回しにしたif文の方です。
せっかくfunctionにするのだから、標準入力もとれるようにしたいと思ったのです。パイプでつなげるのが理想。

なので、functionには[ -p /dev/stdin ] で標準入力を開いているときと、開いていないときとで分岐が入っています。標準入力が開いてないときはelse節のほうにいっています。

しかし、標準入力でを開いているときのほうが、問題でした。これ、二回標準入力をなめないといけないんですね。

if文の前半をtail -n +$(grep -n "$1" - |head -1| sed 's/:.*$//') - こういうふうにするとどうなるかというと、何も返ってこない。
$() の中身のgrepで標準入力が終わりまで読まれているので、 tail -n +行数 - したときには標準入力は空っぽなのです。
標準入力を読み終わったあと頭に戻せないかいろいろ考えてみましたが、今の所見つからず。やむを得ず、標準入力が開かれていたときは一旦tmpファイルに内容を書き出して、それを使って読み込みしてます。
どでかいファイルを標準入力で渡したときに、大きいtmpファイルを作っちゃうじゃないか、とか、tmpファイルが残っちゃったらどうするか、とか、いろいろ問題はあるんですが。

別解

こうすればできるとコメントをいただきました。ありがとうございます。

awk

$ awk '/キーワード/,/*/ {print}' ファイル名 

なるほどー。
awkの範囲パターンを使って、キーワードのある行からすべての行をパターンに指定しているんですね。これは便利。

ただし、このawk、一部のawkでは動きませんでした(mawk 1.3.3 でNG)。試した範囲で、GNU Awk 3.1.7, GNU Awk 4.2.1 とかでは大丈夫。

sed

$ sed -e '/キーワード/,$p' -e d ファイル名

sedでキーワードのある行から、最後の行まで($)をpで出力し(行の複製)、次の -e d でもともとの行を消しているんですね。
全く思いつきませんでした。
awk力もsed力も高めなければ。

パフォーマンス

やり方が複数あったら計測するよね、ということで。
functionにいれたgreptail, awk, sedを実施して timeコマンドで実行時間をとってみました。

計測1

対象ファイル総行数:205,778行
キーワードから末尾までの行数 :41行

greptail greptail(stdin) awk sed
real 0m0.096s 0m0.305s 0m2.549s 0m0.194s
user 0m0.040s 0m0.044s 0m1.788s 0m0.124s
sys 0m0.026s 0m0.130s 0m0.030s 0m0.013s

計測2

対象ファイル総行数:2,185,828行
キーワードから末尾までの行数 :15行

greptail greptail(stdin) awk sed
real 0m11.194s 0m20.023s 0m26.310s 0m5.772s
user 0m0.378s 0m0.378s 0m18.071s 0m1.417s
sys 0m0.497s 0m1.390s 0m0.342s 0m0.281s

sedが安定した速さという感じですね。

6
4
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4