基本的にワンライナーで使うことを想定してメモ。(長くなる場合はスクリプトの形で記載する。)
awk の良いところ
- Linuxでテキストデータをちゃちゃっと処理できる。
- ほとんどのLinux OSで標準で使える(プレインストールされているので,自分でインストールする必要がない)。
- WindowsのMingwや,Macでも標準で装備されているので互換性や移植性が高い(スクリプトだけコピーして持っていれば,だいたいどこでも使える)。
- 書き方は,Cと似ているので覚えやすい。
- コンパイルが不要。
- codingが簡単なので,作業時間が短く済む。
基本
awkはテキストファイルを,行ごとに処理を行う。
それぞれの要素(列)に対しての処理をcodingすることが基本となる。
それぞれの要素に関しては,以下で紹介するような組み込み変数で指定する。
例えば,テキストファイル内の1列目の全数値データに対して,2を足して出力したいときは,以下のようにする。
awk '{print $1 + 2}' hoge.txt
$1が1列目の要素を表す。
hoge.txtが処理の対象となるテキストファイル。
組み込み変数
-
$1, $2, ..., $n, ...
: 第n列目のデータ -
NF
: フィールド(列)の数(1からスタート) -
NR
: 行番号(1からスタート)
よく使う処理
行全部をそのまま表示したい
(ただファイルの内容を表示するだけなので,cat
と同じ)。
awk '{print $0}' hoge.txt
1列目のデータを百倍して表示したい
awk '{$1=$1*100;print $0}' hoge.txt
テキストファイルの第2列目だけ表示したい
awk '{print $2}' hoge.txt
N行毎に出力したい(行のスキップ・間引き)
10行毎に出力したい場合
以下のようにすると1行目,11行目,21行目...が出力される。(10で割ったときに1余る数)
awk 'NR%10==1' angle_dist.dat > angle_dist_skip10.dat
同様に2行毎の場合はNR%2==1
とする。
第1列目のデータの合計(平均)をとりたい
awk '{sum+=$1} END {print sum,sum/NR}' hoge.txt
- END {} の中に,最後に行いたい処理を書く。
- 勝手に変数sumを初期化してくれている。
明示的に初期化したい場合は,BEGINを使う。
awk 'BEGIN {sum=0} {sum+=$1} END {print sum, sum/NR}' hoge.txt
処理する行の指定
NR==1{
print "hogehoge"
}
NR>1{
print $0
}
ファイルへの出力
{
aa="out.txt"
print "hogehoge" > aa
}
if文
awk '{if( $1 == 0 ){ print $0 }}' hoge.txt
三項演算子
x ? y : z
x が真なら y,偽なら z の値
- 例
print ($1 < $2 ? $1 : $2)
2つの値を比較し,小さい値を出力。
ループ
for
for (i = 1; i <= 10; i++){print i}
文字列の要素を抽出
- substr関数を使う。
- 変数(文字列)のm文字目からn文字目までを取り出したい場合
substr(変数,m,n)
awk '{ print substr($1,1,1) }' hoge.txt
配列
全ての配列は連想配列(他の言語のハッシュ,辞書に相当)として扱われる。
array1[3] = "component1"
実用例(平均値の分布)
2列のテキストデータをインプットに,1列目の値をbinで区切って,
それぞれのbinの中で2列目のデータの平均値を求める。
以下は,bashスクリプトの中に,awkスクリプトを組み込んでいる。
#!/bin/bash -ue
inputfile=angle_velo.dat
outfile=out_do4.txt
width_win=10.0
interval_win=10.0
min_x=-180.0
max_x=0.0
################################
# AWK scripts #
################################
read -d '' scriptVariable << 'EOF'
BEGIN {
for (i=v_min_x;i+v_width_win<=v_max_x;i+=v_interval_win) {
num_sum[i+v_interval_win/2] = 0; #initialization
}
}
{
for (i=v_min_x;i+v_width_win<=v_max_x;i+=v_interval_win) {
if (i<=$1 && $1<i+v_interval_win) {
sum[i+v_interval_win/2] += $2;
num_sum[i+v_interval_win/2] ++;
}
}
}
END {
for (i=v_min_x;i+v_width_win<=v_max_x;i+=v_interval_win) {
if (num_sum[i+v_interval_win/2] != 0){
print i+v_interval_win/2,sum[i+v_interval_win/2]/num_sum[i+v_interval_win/2], num_sum[i+v_interval_win/2]
}
else {
print i+v_interval_win/2,sum[i+v_interval_win/2]
}
}
}
EOF
################################
# End of AWK Scripts #
################################
awk -v v_min_x="${min_x}" \
-v v_width_win="${width_win}" \
-v v_interval_win="${interval_win}" \
-v v_max_x="${max_x}" \
"$scriptVariable" ${inputfile} > $outfile
ランダム数生成
BEGIN{
pi = atan2(0, -1)
while(i < n){
x1 = rand()
x2 = rand()
if(x1 != 0){
print sqrt(-2 * log(x1)) * cos(2 * pi * x2)
i++
if(i >= n) break
print sqrt(-2 * log(x1)) * sin(2 * pi * x2)
i++
}
}
}
このスクリプトを実行するときは, -vオプションで,乱数の個数 n を指定する必要がある. 今回は,100万個の乱数列 nrand.txt を作る.
$ awk -f normal_random_numbers.awk -v n=1000000 > nrand.txt
nrand.txt が,平均 0, 標準偏差 1 の分布に,ほぼ従っていることが確認できる
平均値,標準偏差
{
x[NR] = $1
}
END{
if(NR == 0) exit
for(i in x){
sum_x += x[i]
}
m_x = sum_x / NR
for(i in x){
sum_dx2 += (x[i] - m_x) ^ 2
}
print "average ",m_x
print "variance",sum_dx2 / NR
print "standard_deviation ",sqrt(sum_dx2 / NR)
}
$ awk -f stddev.awk nrand.txt
計算誤差が蓄積してしまうため,
めんどうでも,一度平均値を計算してから,差の 2乗の平均を求めた方が安全である.
パターンマッチ
- 演算子 ~ を使える。
- grepみたいなことをしたい場合。
if( $1 ~ "abc" ) # $1がabcを含んでいれば真
- 裏は,演算子 !~ を使う。
if( $1 !~ "abc" ) # $1がabcを含んでいなければ真
正規表現
if( $1 ~ /[#@]/ ) # $1が#または@であれば真
.xvgファイルのコメント行を除いて出力
awk '{if(substr($1,1,1) !~ /[#@]/)print}' hogehoge.xvg
セパレータの指定
- ,でデータを区切りたい場合
awk -F "," '{print $2}' hoge.txt
応用例
graceプロットデータ(hoge.xvg)のデータ行だけ抽出したい。
- コメント行やラベル行は,@や#から始まるので,それだけ除いて出力する。
awk '{if(substr($1,1,1) !~ /[#@]/)print $0 }' hoge.xvg > out.txt
行の要素全てを足し合わせたい。
awk '{sum=0;for(i=1;i<=NF;i++){sum+=$i}print sum}' hoge.txt
ユーザー定義関数
function myprint(num)
{
printf "%6.3g\n", num
}
次の行の表示
getline関数を使う。
awk '{if( "r_2_3_&_P" == $2 ){getline; print} }' index_axrst.ndx
文字列や実数を整数へ変換する
int( $1 )
例えば,
print int ("38DG")
は,38
と出力される。
処理中に別のファイルを開く
getline
関数を使う。
http://tounderlinedk.blogspot.jp/2010/07/getline-1-awk.html
{
time = $1
atmid1 = $2
atmid2 = $3
while (( getline < "eq2_dna.gro" ) > 0) {
if( $3 == atmid1 ){
#print $1,$2,$3
resid1 = int( $1 )
if ( resid1 > 38 ) resid1 = 77 - resid1
}
if( $3 == atmid2 ){
resid2 = int( $1 )
if ( resid2 > 114 ) resid2 = 229 - resid2
}
}
close("eq2_dna.gro")
print time, resid1 , resid2
}
参考