hirohirohirohirosのブログ

地方国立大学に通う情報系学部4年

シェル・ワンライナー160本ノック week3 問題5~問題11

問題5

 まずntp.confを表示します.

$cat ntp.conf
# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help

driftfile /var/lib/ntp/ntp.drift

# Enable this if you want statistics to be logged.

次に1列目がpoolである行のみを出力します.

$ cat ntp.conf | awk '$1=="pool"'
pool 0.ubuntu.pool.ntp.org iburst
pool 1.ubuntu.pool.ntp.org iburst
pool 2.ubuntu.pool.ntp.org iburst
pool 3.ubuntu.pool.ntp.org iburst
pool ntp.ubuntu.com

これの2列目の値がサーバーの名前らしいので2列目を出力します.awkのprintを使います.

$ cat ntp.conf | awk '$1=="pool"'| awk '{print $2}'
0.ubuntu.pool.ntp.org
1.ubuntu.pool.ntp.org
2.ubuntu.pool.ntp.org
3.ubuntu.pool.ntp.org
ntp.ubuntu.com

問題6

 man seqしてseqの仕様を見るとseq [OPTION]... FIRST INCREMENT LASTとあるので,seq 5 -1 1とすれば,

seq 5 -1 1
5
4
3
2
1

と逆順で表示されます.これを使えば

$ seq 5 -1 1 | awk '{for(i=1;i<$1;i++){printf " "};print "x"}'
    x
   x
  x
 x
x

となります.(別解4を自力で求められました)

問題7

awk -F

awkにFのオプションを付ける事で列の区切り文字を空白から変更できます.

$ echo "sarasara" | awk -Fr '{print$1, $3}'
sa a

sarasaraという文字列をrで区切ることによりsa, asa, aとなり,$1と$3をprintするのでsa aとなります.

NF

 NFで列数を表します.

$ echo "sarasara" | awk -Fr '{print NF}'
3

sa, asa, aと区切られるのでNFは3です.これを$()で加工と変数を動的に指定することが出来ます.

$ awk -F: '{print $(NF-2)}' access.log
22
02
14
13
09

別解として,文字列を切り出す方法を調べたので紹介します.

substr

 awkにはsubstrという文字列を切り出す関数があります.substr(切り出したい文字列,切り出す位置,切り出す文字の長さ)とします.access.logについて,午後の情報があるのは全て4列目なので,まず4列目を取り出し,時間の情報は全て14文字目からなので

$ cat access.log | awk '{print $4}'|awk '{print substr($1, 14, 2)}'
22
02
14
13
09

となります.その後は解答と同じようにすると

$ cat access.log | awk '{print $4}'|awk '{print substr($1, 14, 2)}'|awk '$1<12{print "gozen"} $1>=12{print "gogo"}'|sort|uniq -c
      3 gogo
      2 gozen

問題9

seq -n '//p'

 seqに -nというオプションを付けると対象の文字列のみ表示するという処理になります.この対象に'/正規表現1/, /正規表現2/p'とすると正規表現1にマッチする行から正規表現2にマッチする行までを抽出するようになります.
 今回の問題のように時系列順に並んだファイルなら,時刻を正規表現で表し,時刻aから時刻bまで抜き出すということが出来そうです.

$ cat log_range.log | sed -n '/24\/Dec\/2016 21:..:../, /25\/Dec\/2016 03:..:../p'
192.168.77.248 - - [24/Dec/2016 21:12:20] "GET / HTTP/1.0" 200 4294
192.168.152.143 - - [24/Dec/2016 22:06:19] "GET / HTTP/1.0" 200 7255
192.168.6.132 - - [24/Dec/2016 23:00:42] "GET / HTTP/1.0" 200 4298
192.168.222.3 - - [25/Dec/2016 00:03:23] "GET / HTTP/1.0" 200 8547
192.168.101.95 - - [25/Dec/2016 01:01:40] "GET / HTTP/1.0" 200 8488
192.168.141.18 - - [25/Dec/2016 02:15:52] "GET / HTTP/1.0" 200 4533
192.168.110.169 - - [25/Dec/2016 03:06:54] "GET / HTTP/1.0" 200 3461

問題10

 解答の正規表現が複雑なので1つずつ解説します.
 ^は先頭の文字を表すので,^##は先頭が##の文字となります.+は直前の文字が1個以上ある場合にマッチします.つまり +は空白文字が1個以上存在する文字になります.今回の場合## Aや## Aは対象の文字になり得ますが,A##や##Aは対象の文字にはなりません.
 .は任意の文字,*は0回以上繰り返しになります.つまり.*はなんらかの文字(文字がなくても良い)となります.それを()でかこっているので^## +(./)は先頭は##でその直後空白が1つ以上ある文字列で,空白後にある何らかの文字列を参照するとなります.
 \1\n---について,まず\1は先ほど()で参照した文字が入ります.そして\nは改行文字です.よって/^## +(.*)/\1\n---は^## +(.*)を(.*)で置き換え,改行し---とする処理になります.

問題11

 この問題で難しかったのは改行の削除の仕方と改行の入れ方だと思います.今回は改行を削除するという考え方では無く,発言内容が1行しか無いことを利用して2行ずつ並べて出力するxargs -n2を使います.
 改行の入れ方は,文字列を$1,改行を\nとしてseq 's/$/\n/とします'.seqを連続して行うにはパイプを繋げるほかに;を使う方法があります.

$ cat gijiroku.txt | sed 's/すず/鈴木/'| sed 's/さと/佐藤/' | sed 's/やま/山田/'| xargs -n2 | sed 's/ /:/'| sed 's/$/\n/'
鈴木:あばばあばば

佐藤:あばばばばばばば!

山田:びっくりするほどユートピア!びっくりするほどユートピア!

鈴木:うひょひょひょwwwww山田wwやまwww

佐藤:ひょおお?ひょおお???

鈴木:それでは会議を終わります

;で繋ぐパターン

$ cat gijiroku.txt | sed 's/すず/鈴木/; s/さと/佐藤/;s/やま/山田/'| xargs -n2 | sed 's/ /:/;s/$/\n/'
鈴木:あばばあばば

佐藤:あばばばばばばば!

山田:びっくりするほどユートピア!びっくりするほどユートピア!

鈴木:うひょひょひょwwwww山田wwやまwww

佐藤:ひょおお?ひょおお???

鈴木:それでは会議を終わります