調査とかでこんな感じのファイルに対してgrepなりしたりする場合
access_log.2015-08-01.gz access_log.2015-08-02.gz access_log.2015-08-03.gz access_log.2015-08-04.gz access_log.2015-08-05.gz access_log.2015-08-06.gz access_log.2015-08-07.gz access_log.2015-08-08.gz access_log.2015-08-09.gz access_log.2015-08-10.gz
今まではスクリプトでやっていました。
毎回スクリプトに書くのも面倒だと思ったので、割と汎用的に使えるスクリプトを作ろうと思って作ってみました。
要件としては
- 連続した日付のログに対して同じコマンドを発行して結果を得たい
スクリプトでの実装
- date-seq-exec
#!/bin/sh _usage() { cat <<_EOT_ Usage: $0 [-s datetime] [-e datetime] [-f format] [-c command] Description: this script is handle for the file involved continuation date. Options: -s start date. Required -e end date. Required -c execute command. please single quotation. Required please ues '{}' replace date string for command. in command. -f datetime format in shell. default is '+%Y-%m-%d' _EOT_ exit 1 } # default date format FORMAT='+%Y-%m-%d' while getopts s:e:f:c: opt do case ${opt} in s) set_options='y' STARTDATE=${OPTARG};; e) set_options='y' ENDDATE=${OPTARG};; f) set_options='y' FORMAT=${OPTARG};; c) set_options='y' COMMAND=${OPTARG};; :|\?) _usage;; esac done [ "${set_options}" != 'y' ] && _usage CURDATE=$STARTDATE while [ 1 ]; do curdate_s=`date -d "$CURDATE" '+%s'` enddate_s=`date -d "$ENDDATE" '+%s'` diff=`expr $enddate_s - $curdate_s` datestr=`date -d "$CURDATE" "$FORMAT"` ${COMMAND//\{\}/$datestr} if [ $diff -le 0 ]; then break fi CURDATE=`date -d "$CURDATE 1day" "+%Y-%m-%d"` done
date-seq-exec -s 2015-08-01 -e 2015-08-10 -f '+%Y-%m-%d' -c 'wc -l /data/var/log/applog/access_log.{}.gz'
こんな感じで実行することができます
これで一定期間中のdatetimeに関する情報を使っているファイルに対しての処理を行う場合はあまり意識しなくてもよくなります
ワンライナーでの実装
ただ、そもそもそういう事考える人いるんじゃないか?と思いなおし調べたところやっぱりありました。。。
上記ページのseqとforの組み合わせ + xargs -i でやりたいことはもはやワンライナーで書けます
for i in `seq 0 9`;do date +"%F" --date "20150801 $i days";done | xargs -i wc -l /data/var/log/applog/access_log.{}.gz
xargs は-iオプションを使うことで、パイプで受け取った出力を{}で置き換えることができます
また、複数コマンドやパイプを使いたい時などは sh -c
で実行するスクリプトを渡してあげればいいようです
for i in `seq 0 9`;do date +"%F" --date "20150801 $i days";done | xargs -i sh -c 'grep "hoge" /data/var/log/applog/access_log.{}.gz | grep " 500 "'
awkとかも使うことができます、これで特にツールも入れずに調査はポチして待つだけ、が実現できますね!
for i in `seq 0 9`;do date +"%F" --date "20150801 $i days";done | xargs -i sh -c 'ssh 10.0.0.1 grep "hoge" /data/var/log/applog/access_log.{}.gz | awk "{print \$14}" | awk -F"[=&]" "{sum+=\$8;}END{print \"{}\" sum \"\t\" NR}"'
はまったところ
この方法に限った事ではないですがシェルスクリプトとシェルで実行するのとで、若干違いがあってそこではまることが多々ありました
特にawkで良くはまったので残しておきます
- 変数
awk '{print $14}'
awk '{print \$14}'
- クォーテーション
END{print sum \t NR}
END{print sum \"\t\" NR}
awkでの変数として認識させるところとクォーテーションを認識させてあげるところですね
わかっていても時間とられることが多かったです。。。
並列実行
xargsの-Pオプションで並列実行もすれば時間短縮も図れます、無駄に長いスクリプトより便利そう
-P0
で最適なプロセス数で実行してくれるようです
ローカルからxargsでssh叩く場合はちょっと気を付けたほうがいいですが、サーバ1台で完結する場合なら基本的には0でいいのかなと思います
日付以外にも
例では日付に関するファイルで色々やりましたが、サーバ一覧をとってきてそれらに対して一斉にコマンド打つとかも簡単にできますね
ansibleあるじゃんって話でもありますが、まぁコード書いて何とかするほどではないかなっていう感じのレベルならこちらの方が早いと思います
今までスクリプト書いてましたがスクリプト書く必要がなくなります
まとめ
forとseqは結構便利、そしてxargsと組み合わせるともっと便利!
まだまだこういう便利な使い方があることを知らずに無駄に行っていることが沢山あると思うのでもっと理解を深めて効率化を図りたいですね