理科系の備忘録

Linux/Ubuntu/Mac/Emacs/Computer vision/Robotics

awkとかsedとか

AWKUNIX上で開発されたテキスト処理スクリプト言語である.sedgrepと並んで3種の神器なんて呼ばれていたこともあるそうです.今回はawkGNU版であるgawkを使う.


awkによる行・列の指定

-rw-r--r--   1 hogehoge  staff       21  6 27 14:55 #blog_draft#
drwx------@ 39 hogehoge  staff     1326  6 27 14:58 .
lrwxr-xr-x   1 hogehoge  staff       30  6 27 14:54 .#blog_draft -> hogehoge@Kenta.local.3847
drwxr-xr-x+ 54 hogehoge  staff     1836  6 27 13:06 ..
-rw-r--r--   1 hogehoge  staff    15364  6 26 12:35 .DS_Store
-rw-r--r--   1 hogehoge  staff    42770  6  7 11:37 .bash_history
-rw-r--r--   1 hogehoge  staff      220  4  4  2011 .bash_logout
-rw-r--r--   1 hogehoge  staff      104  4 12 11:37 .bash_profile
-rw-r--r--   1 hogehoge  staff     3510  5 24 10:59 .bashrc
drwxr-xr-x   5 hogehoge  staff      170  6 27 14:18 .dropbox.cache
-rw-r--r--   1 hogehoge  staff     5041  6 25 11:57 .emacs
-rw-r--r--   1 hogehoge  staff     5098 11 12  2011 .twmrc
-rw-r--r--   1 hogehoge  staff      343 11 12  2011 .xinitrc
-rw-r--r--   1 hogehoge  staff     3692  3 26 19:42 CF-T5_1.icm
-rw-r--r--@  1 hogehoge  staff        0  4 17 17:47 Icon
-rw-r--r--@  1 hogehoge  staff      524  6 25 10:46 Makefile
drwxr-xr-x@ 10 hogehoge  staff      340  6  5 09:44 Photos
-rw-r--r--@  1 hogehoge  staff      285  6 25 10:46 README.txt
-rw-r--r--   1 hogehoge  staff      238  6 25 10:46 TODO.txt
drwxr-xr-x  18 hogehoge  staff      612  6 25 10:46 ac-dict
-rw-r--r--   1 hogehoge  staff      132  9 15  2011 account.txt
-rw-r--r--   1 hogehoge  staff        0  6 27 14:54 blog_draft

これをll.datとする.このファイルから指定の行や列を指定して値を抽出,置換したりしてみようと思う.

まずll.datから指定の列だけを表示する.余談だが,列が縦並び行が横並びなのはいいとして,行列の添字のどちらが列番号なのか未だに混乱する.rと↓,cと←,というように形状から覚えるのがセオリーか.

$ gawk '{print $1}' < ll.dat

total
-rw-r--r--
drwx------@
lrwxr-xr-x
drwxr-xr-x+
…

見て分かる通り$1を表示しますという命令をll.datに対して行なっている.列番号は左から順に$1から格納されている.つまり

    $1     | $2 |    $3    |  $4   | $5 | $6 | $7 |  $8   |      $9
-rw-r--r-- |  1 | hogehoge | staff | 21 |  6 | 27 | 14:55 | #blog_draft#

というようになる.print $9などとればファイルネームだけを取り出すことができる.
一方,行は条件式という形で指定する.

$ gawk 'NR==5 {print $0}' < ll.dat

Drwxr-xr-x+ 54 hogehoge  staff     1836  6 27 13:06 ..

数行まとめて表示するには

$ gawk 'NR<10 && NR>20 {print $0}' < ll.dat 

とすればOK.

NRは特別な変数であり行番号を指定することができる.{}の左側には条件式を書くことができ,そこで行番号を指定する.
なお,printに渡した変数$0にはすべての数字が格納されている.

これで行と列が指定できるようになったので任意の要素へアクセスすることができる.

$ gawk 'NR==5 {print $5}' < ll.dat

1836

正規表現を用いる場合は

$ gawk '$0~/.*txt/ {print $0}' < ll.dat 
-rw-r--r--@  1 hogehoge  staff      285  6 25 10:46 README.txt
-rw-r--r--   1 hogehoge  staff      238  6 25 10:46 TODO.txt
-rw-r--r--   1 hogehoge  staff      132  9 15  2011 account.txt

などとすればよい.

[追記:2012.07.08]列を指定する変数 => NR



awkやらsedやらで置換

次に文字列の置換を行う.

$ gawk '$2==1 {$2=5;print $0}' < ll.dat 

-rw-r--r-- 5 hogehoge staff 21 6 27 14:55 #blog_draft#
lrwxr-xr-x 5 hogehoge staff 30 6 27 14:54 .#blog_draft -> hogehoge@Kenta.local.3847
-rw-r--r-- 5 hogehoge staff 15364 6 26 12:35 .DS_Store
-rw-r--r-- 5 hogehoge staff 42770 6 7 11:37 .bash_history
...

「2列目の要素が1のものだけを表示して,その要素に5を代入」という処理である.かなり直感的で嬉しい.しかしまあ置換を行う場合はawkよりもsedの方がずっと簡単である.

sedの簡単な使い方は,以下のように-nオプションで行数を指定して出力するというものである

$ sed -n '5,10p' ll.dat

drwxr-xr-x+ 54 hogehoge  staff     1836  6 27 13:06 ..
-rw-r--r--   1 hogehoge  staff    15364  6 26 12:35 .DS_Store
-rw-r--r--   1 hogehoge  staff    42770  6  7 11:37 .bash_history
-rw-r--r--   1 hogehoge  staff      220  4  4  2011 .bash_logout
-rw-r--r--   1 hogehoge  staff      104  4 12 11:37 .bash_profile
-rw-r--r--   1 hogehoge  staff     3510  5 24 10:59 .bashrc

置換を行う場合は以下のようにsとgで置換前と置換後の語を指定する.

$ sed 's,r,R,g' ll.dat

total 9408
-Rw-R--R--   1 hogehoge  staff       21  6 27 14:55 #blog_dRaft#
dRwx------@ 39 hogehoge  staff     1326  6 27 14:58 .
lRwxR-xR-x   1 hogehoge  staff       30  6 27 14:54 .#blog_dRaft -> hogehoge@Kenta.local.3847
dRwxR-xR-x+ 54 hogehoge  staff     1836  6 27 13:06 ..
-Rw-R--R--   1 hogehoge  staff    15364  6 26 12:35 .DS_StoRe
-Rw-R--R--   1 hogehoge  staff    42770  6  7 11:37 .bash_histoRy
...

Perlなどと同様の置換方法となっている.つまり's/r/R/g'でもs|r|R|g'でも実行することができる.gは一括変換を意味し,もし's,r,R,'とすれば

$ sed 's,r,R,' ll.dat 
total 9408
-Rw-r--r--   1 hogehoge  staff       21  6 27 14:55 #blog_draft#
dRwx------@ 39 hogehoge  staff     1326  6 27 14:58 .
lRwxr-xr-x   1 hogehoge  staff       30  6 27 14:54 .#blog_draft -> hogehoge@Kenta.local.3847
... 

のように一番初めのrがRに変換されることになる.