awk, sedのメモページ

awkについてのメモページ。

よさげなサイト

参考にできそうなサイト群。

sedを使うときは一読すると便利かも。

Awkの基本

Awkの基本は、パターン・アクション構造を持っているということである。たとえば、

awk '$1>1.0 {print $2}' hoge.txt

などのように、最初の

$1>1.0

の部分(パターン)と、次の

{print $2}

という部分(アクション)に分かれる。このように、Awkの構造は、

パターン { アクション }

という形で表される。ちなみに、パターンもアクションも省略可能なので、違いをはっきりとさせるために、アクションには{}がつく。

Awkの使い方。

コマンドラインでawkを使いたいときは、

awk ' programme ' hoge.txt

のように、awkプログラムを各部分は、''でくくる。
長いawkプログラムを書く場合、コマンドラインでいちいち描いていられないので、例えば、hoge.awkなどというファイルにプログラムを書き、

awk -f hoge.awk hoge.txt

というように、-fオプションをつけて、awkプログラムだよ、ということを教えてあげればよい。

Awkの読み込み方

Awkは、ファイルを上から一行ずつ読み取り、それをタブや空白で分解し、欄として名前を付けていく。例外として、BEGINやENDなどがあるが、それらについては、あとで取り上げる。最初の欄は$1, 次は$2, などといった具合。ちなみに、その行全体は、$0に入っている。

列を切り抜く

ここから、実際にAwkを使って、いろいろな操作をしていく。
たとえば、全部でn列あるテキストファイルfile1.txtがあるとする。
そのファイルから、n1,n2,n3列目だけをとりだしたい時は、

awk '{print $n1,$n2,$n3}' file1.txt > file2.txt

などとしてやると、無事、n1列目とn2列目と、n3列目だけが抜
き出され、新たなテキストファイルが作られる。

パターンのみを使って、表示

アクションを省いて表示することもできる。この場合、例えば、

NR == 100

は、

NR == 100 {print $0}

と同じ意味となる。

列の数を調べる。

とあるテキストファイルの、列の数がどれだけあるかは、NFという変数に入る。なので、

awk 'END {print NF}' hoge.txt

などとすると、hoge.txtの列の数が表示される。ちなみに、最後の列を表示したいときは、

awk '{print $NF}' hoge.txt

とすればよい。

行の数を表示する。

Awkには、読み込んだ行の数をNRという変数に入れている。なので、

awk 'END {print NR}' hoge.txt

などとすれば、hoge.txtには何行あるかが、一発でわかる。

とある列の文字列の長さを表示する

文字列の長さを求めたいときは、組み込み関数lengthを使えば良い。$1に天体名が入っており、その文字列の長さを求めたいときは、

awk '{print $1, length($1)}' hoge.txt

などとすればよい。

BEGINとEND

さきほど、ENDというものがでてきた。Awkは原則一行ずつテキストを読み取り、処理をして行く。この読み取りの前、あるいは読み取りがすべて終わった後に処理をしてほしいものがあるとする。それらを書きたい場合に、BEGINとENDを用いる。
たとえば、BEGINの使い方としては、

BEGIN {printf "%6d, %6d, %6d",A,B,C}

などのように、最初に題名などをつけたいときや、

BEGIN {FS = "\t"}

などのように、列を区切るものをきちんと指定したい場合、によく使われる。ここで、FSというのは、列を区切るものを何にするかが入っている変数で、\tで、区切るものとしてタブを指定している。
一方、ENDの使い方としては、先ほどのNRなどのように、

END {print NR}

のように、行数を表示したりする場合など、すべての処理が終わった際に与えられるものに対して、よく用いられる。

printとprintfの違い


プログラム例

以下、何個かプログラムを書いてみる。あくまで参考程度に。間違っていたらごめんなさい。

列を交換する

3列しかないテキストファイル (hoge1.txt) の、1列目と2列目を交換するには、

awk '{print $2,$1,$3}' hoge1.txt

とすればよいが、例えば10列あるテキストファイル (hoge2.txt) の、1列目と2列目を交換する場合、

awk '{print $2,$1,$3,$4,$5,$6,$7,$8,$9,$10}' hoge2.txt

とするのは、ちょっとめんどくさい。こういう場合は、

awk '{ kari=$2; $2=$1; $1=kari;print}' hoge2.txt

としておけば、何列あるテキストファイルにも再利用ができるかも。

数え上げをする


例えば、天体名($1)とredshift ($2)が入ったファイル(hoge.txt)があるとする。今、z>0.1以上の天体の数を数えたければ、

BEGIN {redshift = 0.1}
$2 > redshift { n = n+1 }
END { print "The number of objects over",redshift,"is:",n}

というプログラム(number.awk)をかいて、

awk -f number.awk hoge.txt

などとすれば、数えてくれる。あとは、redshiftをかえて行けば良い。

タブ出力をする

awkはデフォルトでは、空白出力なので、タブ出力をしたいときは、

awk 'BEGIN{OFS="\t"} {print $1,$2,$3}'

などというように、BEGINを使う。

平均と分散


awk '$3>-40 {print $3}' sun2220110531.txt | awk 'BEGIN{sum=sum2=0};{sum=sum+$1;sum2=sum2+$1*$1};END {ave=sum/NR;sigma=sqrt(sum2/NR-(sum/NR)^2);print NR,ave,sigma}'

tex用のtableを作る

texは基本的に、&区切りなので、データがスペース区切りだったりカンマ区切りだったりしたら、適宜tex用の区切りに変える必要がある。とあるデータの$1,$2,$3のデータを使ってtexのtableの書式に書き換えるには、

awk 'BEGIN{OFS=" & "};{print $1,$2,$3" \\\\"}'

とすればよい。OFSで、区切りを" & "で出力することを約束させている。print文の最後の" \\\\"は、texの改行"\\"を行末に表示させている。

また、printf文を使って有効数字を揃えてtex用のtableを作りたいときがある。printf文はOFSが(理由はしらないが)通らないので、上のが少し汚くなる。

awk '{printf("%s %4.3lf %4.3lf\n", $1,$2,$3)}' | sed -e "s/ / & /g" | awk '{print $0" \\\\"}'

という感じ。汚いなぁ。

文字列を表示する

1列目にある文字列を表示するのは
awk '{print $1}' hoge.txt
で可能だが、たとえば、文字列を10桁の欄に左寄せで表示したい場合は、printfを使って、
awk '{printf("%-10s\n", $1)' hoge.txt

とすればよい。出力書式をカスタマイズして表示したい場合は、基本的にprintfがその要望を叶えてくれる。

特定の文字列のみを表示する

たとえば、$1に天体名が入っていて、MとNから始まる天体名だけ(MCG+02-04-025やNGC4151など)の情報をピックアップしたい場合、
awk '$1 > "M" && $1 < "O" {print}' hoge.txt
とすればよい。文字列の比較は、だいたい辞書順である。

区切り文字を指定する

入力の区切り文字(セパレータ)を指定したい場合は、-Fオプションを用いる。
awk -F "|" '{print $1}' hoge.txt
などとすると、"|"(バー)をファイルの区切り文字として区切ってくれる。

文字を連結する

とある列の文字だけを連結して表示したいときがあるとすれば、
awk '{name = name" "} END{print name}" hoge.txt
とすればよい。name" "ではなく、" "nameとすると、一番最初に空白が入ってしまうので注意。

最大値の導出

とある列($2としよう)の最大値を求め、その時の天体名 ($1に入ってることにし、天体名の最大長が14文字とする) を求めたいときは
awk '$2> max {max=$2;objn=$1} END{printf("%-14s %e\n", $1,$2)}' hoge.txt
とすればよい。

バイト数で区切られたデータをうまく抽出する

世の中には空白やタブ区切りやbar区切り以外にも、バイト数で区切られたデータテーブルが存在する。こういう場合、区切られたバイト数内の文字列には複数の空白が混じっていたりして、しかもその空白の数が列ごとに異なっているということもよくある。そのようなときにはふだんの"awk -F"の区切り文字を変更するやり方ではうまくいかない。awkには、こういう固定長バイトのレコードを各フィールドに分割するやり方も存在する。

それは、最初のBEGIN{...} の...の部分に、各バイト長を以下のように記述してやればよい。

awk 'BEGIN{FIELDWIDTH n1 n2 n3 ...}{print $1}' hogehoge

ここで、n1, n2, n3は各バイト長の数字が入る。具体的に試してみる。

今、Melendez et al.(2015)のtableを見てみよう。
http://iopscience.iop.org/0004-637X/794/2/152/suppdata/apj501268t1_mrt.txt

このデータテーブルはバイト数で区切られており、最初に天体名が入っている。今回の場合、天体名にはNGC 1365のように空白一つの類のものもあれば、MCG+01-57-016のように空白なしのものもある (MCG+01-57-016は本来であれば、MCG +01-57-016が正しい表記であるが、まぁ今回はそこは本題ではないので気にしない)。こやつの天体名をうまく取り出したい。その場合は、こうすればよい。

file名をMel15_tbl1.txtとすると、

awk 'BEGIN{FIELDWIDTHS="24 1 3 3 5 2 2 3 3 7 11 2 7 7 2 7 7 8 12 12 2"}{print $1}' Mel15_tbl1.txt

でうまく取ってこられるはず。各フィールドのバイト数については、テーブルの最初に説明がある。
最終更新:2015年08月04日 15:26