2013/03/27

[Shell] 文字列の先頭を大文字にする


$ echo ${str} | awk '{print toupper(substr($1,1,1))substr($1,2)}'

[Shell] 文字列の先頭N文字を削除する

基本的に正規表現.それ以外の方法があれば随時追記.

$ echo ${str} | sed -e 's|.\{N\}||'

$ expr ${str} : '.\{N\}\(.*\)'

$ echo ${str} | perl -pe 's/.{N}//'

正規表現はやはりperlが見やすいですはい.

2013/03/08

[Shell] 変数をエコーする際に気をつけること

基本的なことですが,この前少しハマったので自分を戒めるためにメモ.

敵は足元にいた

変数をエコーする際に気を付けることは...ずばり,
"改行文字を含んでい(て,かつ表示に反映させ)るならエスケープしよーぜ!"
何言ってんねんこいつ.とりあえずハマった事例を簡単にしてみます.例えば,
foo.txt
1
2
3
なんてファイルがあるとします.その内容を変数に格納してechoで出力してみます.
$ hoge=`cat  foo.txt` | echo ${hoge}
1 2 3
改行されず,代わりに空白が挿入されて1行になってしまいました.これで随分悩まされました.これに遭遇した時はバッククォート内のコマンドが長い上に正規表現で置換までしていたので,そのうちのどこで改行が空白に置換されてしまったのかと(汗)けれど原因は冒頭に示した通り,改行がエスケープされていなかっただけ.ふっつーにバッククォート内の箇所をコマンドライン上で再現したら出力に問題は無かったのです.じゃあこれはechoだなと.つまり以下のようにechoする変数をダブルクォートで囲んで改行文字をエスケープしてやればいいわけでした.
$ hoge=`cat foo.txt` | echo "${hoge}"
1
2
3
もう少し問題箇所の切り出し方を考えながらやるよう反省です.

2013/03/07

サーバのログを加工してみた

 サーバへ飛んでくる不正アクセスと,そのログ.iptablesを使って指定した国からのアクセスを弾き,ログを残すよう設定してあるのだけれど,時間帯別/国別の頻度を見てみたいなーと思っていました.そこで今回はshellの練習も交えてログ解析の(簡単!お粗末!な)スクリプトを作成しました.

材料

syslogの吐く/var/log/messagesから.1アクセスにつき残るログはこんな感じ.

/var/log/messages-20130303:Mar  2 11:21:59 server kernel: [IPTABLES DENY_COUNTRY]:IN=eth0 OUT= MAC=x:x:x SRC=61.236.64.56 DST=xxx.xxx.xxx LEN=48 TOS=0x00 PREC=0x00 TTL=112 ID=61662 PROTO=TCP SPT=13548 DPT=22 WINDOW=65535 RES=0x00 SYN URGP=0

フォーマットはデフォルトのままです.とりあえずここからアクセス元IPアドレス("SRC=xxx.xxx.xxx.xxx")を抽出してみます.

調理その1 - 時間帯別の集計

道具は当然shell.まずはログから1時間毎のアクセス頻度を集計してみます.最近ようやく慣れてきたけど全然美しくないコマンドは以下のようになりました.

$ grep "IPTABLES DENY_COUNTRY" ${LOG}/messages | awk '{print $3}' | cut -f1 -d':' | sed 's/^0\([0-9]\)/\1/' | sort -n | uniq -c | awk '{print $2 " " $1 }'

0 4
1 4
2 6
3 7
~中略~
21 6
22 5
23 10


1)ログから時刻を抽出して,2)重複している時刻をカウントし,3)整形して出力 という感じ.最初はsortとuniqの順序が逆だったのですが,これだと重複が正しく処理されませんでした.なので,uniqの前に必ずsortをかけましょー.
数字を羅列しただけだとわかりづらいですが,時間帯による頻度の差はそれほど無いように思えます.

調理その2 - 国別の集計

続いて,アクセス元の国名から集計します.必要なのは

  1. ログからのアクセス元IPアドレスの抽出
  2. IPアドレスから国名を判定
の2点です.1はOKでしょう.2はとりあえず国名がわかれば良いのでwhoisを使ってみます.これはCentOS6にデフォルト入っていないので,jwhoisをインストールしました.

$sudo yum -y install jwhois

試しに自分のドメインを照会してみました.

$whois hogehoge.net
・・・中略・・・
Tech Country: JP
・・・中略・・・

どうやら"Tech Country"で絞ってやれば取得できそうです.ログに残っている適当なIPアドレスを使って

$whois xxx.xxx.xxx.xxx | grep "Tech Country" ${LOG}/messages

が,何も出力されない.まさかと思って単純にwhoisで照会すると,なんとフォーマットが異なっていました.さらにはフィールドに国名すら載っていない国もある.はてさて困った.けどちょちょっと調べたらgeoiplookupなるコマンドを発見.さっそくインストール.

$sudo yum -y install geoip

$geoiplookup hogehoge.net
GeoIP Country Edition: JP, Japan

$geoiplookup somewhere@china
GeoIP Country Edition: CN, China

どうやら大丈夫そうです.というわけで長くなりましたが,最終的に作ったスクリプトは以下のようになります.


#!/bin/bash

iaddr_list=(`grep "IPTABLES DENY_COUNTRY" $1 | sed 's/^.*SRC=\(.\+\) DST.\+$/\1/g' | uniq`)
cn_list=()
for iaddr in ${iaddr_list[@]}
do
     cn=`geoiplookup ${iaddr}`"\n"
     cn_list=("${cn_list[@]}" $cn)
done
echo -e "${cn_list[@]}" | sed '/^\s*$/d' | awk '{print $4}'  | sort | uniq -c | sed 's/\(.\+\),$/\1/g' | awk '{print $2 " " $1 }'

1)ログから抽出したIPアドレスのリストを配列に格納して,2)それぞれに対して国名を照会し,3)整形して出力という感じです.for文を使ったためちょっと見づらいですが,練習にはちょうどよしでした.いままでパイプに頼りすぎてfor文をまともに書いたことなかったので(...でもfor文がいらなくなる方法やらコマンドありそう,絶対).以下,結果です.


BR 2
CN 96
IP 2
KR 11
RU 7
TW 5

上手くいきました."IP"は国名が見つからなかったアドレスです.国別の結果はわかりやすく,中国からのアクセスが圧倒的です.

今回初めて実用的...かどうかは置いといて,ログをいじくって簡単な集計をしてみました.なるべく他人のブログなどなど見ないようにしてやり通しましたが,やっぱり自分はまだまだshellのレベルが低いことを痛感します.抽出したデータも見える情報少ないですしね.けれどもなかなか収穫も多かったように思います.もう少し発展させて,母数を増やしてきちんと分析・考察できたら楽しいだろうな.

とりあえずやってみました,以上です.