awk anchor.png

awk(オーク)*1とは数値や文字列が規則正しく並んでいるテキストファイルに対して,以下の操作を行うのに適したコマンド(スクリプト言語)で,1977年から利用されている最古参のスクリプト言語。

  • 編集(一部分の取り出し,並べ換え)
  • 簡単な数値の計算(表計算)
    簡単な処理ならばC言語等でプログラムをするよりも,手早く行なうことができる。

文法はC言語に似ているんで,比較的覚えやすいかな。

Page Top

簡単な使用 anchor.png

Hello worldを表示するプログラムは次のようになる。スクリプトファイルの拡張子は特に決まりがない。
hello.awk

{ print "Hello world!" }

awkはgrepやsedのようなフィルタコマンドとして作られているので,何らかの入力が必要。

$ echo | awk -f hello.awk

コマンドラインにプログラムを直接指定することもよく利用される。

$ echo | awk '{ print "Hello world!" }'

ls -lの結果から所有者とグループとファイル名を表示する。

$ ls -l | awk '{ print $3, $4, $9 }'
yueno Administrator hello.awk
yueno Administrator sample.awk

ファイル名がhello.awkだけ表示する。

$ ls -l | awk '$9=="hello.awk" { print $3, $4, $9 }'
yueno Administrator hello.awk

/etc/passwdを:をデリミタとしてユーザ名とホームディレクトリのみを表示する。

$ cat /etc/passwd | awk -F: '{print $1, $6}'
yueno /home/ueno

/etc/passwdのユーザ名とホームディレクトリを整形して表示する。

$ cat /etc/passwd | awk -F: '{printf("%-8s %s\n", $1, $6)}'
yueno    /home/ueno

df実行結果から2行目以降の第2カラムの合計を表示する

$ df | awk 'NR>=2 {z+=$2}; END {print z}'
2850302964
Page Top

実行方法 anchor.png

プログラムはコマンドラインに直接指定するか,-fオプションでプログラムファイル名を指定します。
入力ファイルは標準入力または引数としてファイル名を渡す。

$ cat data.txt | awk '{ print $1, $2 }'            # プログラムを直接指定
$ cat data.txt | awk -f sample.awk                 # プログラムファイルで指定
$ awk -f sample.awk data1.txt data2.txt            # 入力データを引数で指定

bashを使っている場合,プログラムファイルの先頭を#!/usr/bin/awk -fとすると,プログラムファイルをコマンドとして直接実行することが出来る。
sample.awk

#!/usr/bin/awk -f
{ print $1, $8 }
$ chmod 775 sample.awk
$ ./sample.awk
Page Top

レコードとフィールド anchor.png

入力データはレコードとフィールドに分割される。
レコードは行,フィールドは空白文字で区切られたカラムに相当する。レコードとフィールドの区切り文字は,ビルトイン変数RSFSで変更することが出来る。

field1  field2  field3		# record1
field1  field2  field3		# record2
field1  field2  field3		# record3

レコード全体は$0,フィールドは$1,$2,$3,...で参照出来る。

{ print $0 }                   # レコード全体を表示
{ print $1, $2, $3 }           # 1,2,3フィールドを表示
Page Top

awkのプログラム anchor.png

awkのプログラムは,「パターン-アクション規則」と「関数定義」でプログラムで記述する。

パターン{アクション}
パターン{アクション}
...
function 名前(引数の並び){ 文 }
function 名前(引数の並び){ 文 }
...

awkは入力レコードを1行読むたびに,パターン-アクションを順に実行する。
パターンが真となるレコードに対して,対応するアクションが実行される。
パターンあるいは{アクション}のいずれかを省略することができる。
パターンを省略するとすべてのレコードにアクションが実行されて,{アクション}を省略するとパターンが真となるレコードを表示する。

Page Top

パターン anchor.png

パターンは次のどれかになる。

BEGIN
END
式
/正規表現/
パターン,パターン

パターンには /.../ で正規表現を記述出来る。

/ABC/ { ... }                  # レコードの中にABCという文字列が含まれていればマッチ

if (...) 文の ... に記述するような条件式を記述することもできる。

$1 == "ABC" { ... }            # 第1フィールドがABCであればマッチ

条件式で正規表現を使用することもできる。~は正規表現にマッチしたらという演算子。

$1 ~ /^#/ { ... }              # 第1フィールド先頭が#で始まっていたらマッチ

条件1,条件2を記述した場合,条件1が真になる行から,条件2が真になる行までマッチする。

$1=="START", $1=="END" { ... } # 1フィールドがSTARTである行から,ENDである行までマッチ

BEGIN,ENDはプログラムの一番最初と一番最後にアクションを実行する。

BEGIN { ... }                  # 最初に1度だけアクションを実行
END { ... }                    # 最後に1度だけアクションを実行

BEGINFILE,ENDFILEは各入力ファイルの開始時と終了時にアクションを実行する。

BEGINFILE { ... }              # 最初に1度だけアクションを実行
ENDFILE { ... }                # 最後に1度だけアクションを実行
Page Top

アクションと文 anchor.png

アクションは文の並び。
文は次のいずれかになる。

式
制御文
入出力文
{文の並び}
空文

文は,改行か;で区切る。;が単独で用いられると,空文を表す。
文の前後には,空行を挿入してもよい。

長い文は,行末に\を置いて次行に続けることができる。
, { && || do elseのあと,およびif( )for( )のあとは,\を置かずに改行しても継続とみなされる。

アクションには,printで値を出力したり,if,whileなどで制御したり出来る。

{
    if ($1 == "D") {           # 1フィールドの値が"D"であれば
        print $2, $3           # 2,3フィールドの値を出力する
    }
}

セミコロン(;)を使用すると、1行に複数の文を記述することができる。

{
    a = 1; b = 2; c = 3;
    print a, b, c
}
Page Top

制御文 anchor.png

制御文は次のいずれかである。

break
continue
do 文 while(式)
exit
exit(式)
if(式) 文
if(式) 文 else 文
for(式;式;式) 文
for(変数 in 配列) 文
next
return
return(式)
while(式) 文

if-else文で最初の文がelseと同じ行にある場合,この文は;で終了するか{ }で囲まないとダメ。

Page Top

基本的な制御構造 anchor.png

文の種類記法
逐次構造(文の並び){文1; 文2; ... 文n}
選択構造(if文)if(式) 文
選択構造(if-else文)if(式) 文1 else 文2
ループ構造(for文)for(式1; 式2; 式3) 文
ループ構造(while文)while(式) 文
ループ構造(do-while文)do 文 while(式)
Page Top

入出力文 anchor.png

入出力文は次のとおりである。

getline	入力レコードを"$0"にセット
getline 式	0または1
文の種類書式戻り値
比較式>=式0または1
式!=式0または1
式==式0または1
配列要素式 in 式0または1
パターンの検査式~/正規表現/0または1
式!~/正規表現/0または1
式+式演算結果
式-式演算結果
式*式演算結果
式/式演算結果
剰余式%式演算結果
累乗式^式演算結果
文字列をつなげる文字式 文字式文字列
Page Top

配列 anchor.png

変数名[添え字]で配列を扱える。

foo[1] = 123
foo[2] = "ABC"
print foo[1]           # => 123
print foo[2]           # => ABC

添え字には文字列を使用することができる。

foo["name"] = "Ueno"
foo["age"] = 64
print foo["name"]      # => Ueno
print foo["age"]       # => 64

配列の個数はlength()で求められる。数値を添え字とする配列は次のようにしてループを回す。

foo[0] = "ABC"
foo[1] = "DEF"
for (i = 0; i < length(foo); i++) {
    print foo[i]
}

添え字が文字列の場合は,次のようにしてループを回す。

foo["name"] = "Ueno"
foo["age"] = 64
for (x in foo) {
    print x "=" foo[x]         # => name=Ueno, age=64
}

配列の中に該当の要素があるかどうかを調べる場合。

foo["name"] = "Ueno"
if ("name" in foo) {
    print "Exist"
}

配列要素を削除するには,deleteを使う。

foo["name"] = "Ueno"
foo["age"] = 64
delete foo["name"]              # foo["name"]を削除
delete foo                      # 配列foo自体を削除
Page Top

多次元配列 anchor.png

次のようにして多次元配列を扱うことができる。

foo[1, 1] = 1001
foo[1, 2] = 1002
foo[2, 1] = 2001
foo[2, 2] = 2002
for (i = 1; i <= 2; i++) {
    for (j = 1; j <= 2; j++) {
        print foo[i, j]
    }
}

配列の配列の場合も可能。

foo[1][1] = 1001
foo[1][2] = 1002
foo[2][1] = 2001
foo[2][2] = 2002
for (i in foo) {
    for (j in foo[i]) {
        print foo[i][j]
    }
}
Page Top

print文 anchor.png

printは,変数や値を出力する。

print $1, $2, $3

値をカンマ,で連結すると,ビルトイン変数OFSに指定された出力フィールドセパレータ(デフォルトは半角スペース)で区切って出力する。

a = "AA"; b = "BB"; c = "CC";
print a, b, c                  # => AA BB CC

OFSを変更することで,区切り文字を変更することが出来る。

a = "AA"; b = "BB"; c = "CC";
OFS = ","
print a, b, c                   # => AA,BB,CC

値をカンマ,で区切らない場合は,文字列の連結が行われた後に出力される。

a = "AA"; b = "BB"; c = "CC";
print a b c                     # => AABBCC
Page Top

組み込み変数 anchor.png

次のような組み込み変数が用意されている。

組み込み変数
ARGCコマンド行の引数の数
ARGVコマンド行の引数の配列
FILENAME現在の入力ファイル名
ENVIRON["..."]環境変数の値
FS入力のフィールドセパレータ(はじめはスペースまたはタブ)
RS入力のレコードセパレータ(はじめは改行)
NF現在レコードのフィールド数
NR現在の通算レコード
FNR現在の入力ファイルの通算レコード
OFS表示のフィールドセパレータ(はじめはスペース)
ORS表示のレコードセパレータ(はじめは改行)
OFMT数の表示のフォーマット(はじめは"%.6g")
RSTARTmatchでマッチした文字列の開始位置
RLENGTHmatchでマッチした文字列の長さ
$0現在の入力レコード
$1,...,$NF第1フィールド,...,第NFフィールド
Page Top

組み込み文字列関数 anchor.png

組み込み文字列変数
gsub(r,s,t)文字列tの中に現れる文字列rをすべて文字列sで置換する。置換した数を返す。tを省略すると$0が使われる。
index(s,t)文字列sの中の文字列tの位置を返す。tが現れない場合は0
jindex(s,t)日本語文字列sの中の文字列tの位置を返す。tが現れない場合は0。
length(s)文字列sの長さを返す。
jlength(s)日本語文字列sの長さを返す。
match(s,r)文字列sが文字列rにマッチする位置を返す。マッチしないときは0。
split(s,a,fs)fsをフィールドセパレータとして文字列sを配列aに分解し,フィールド数を返す。
sprintf(書式,式)書式で整えた式の並びを返す。
sub(r,s,t)gsub()と同様。ただしはじめの1回だけ置換する。
substr(s,i,n)文字列sのi番目から始まるn文字を返す。
jsubstr(s,i,n)日本語文字列sのi番目から始まるn文字を返す。
Page Top

組み込み算術関数 anchor.png

組み込み算術変数
atan2(y,x)atan(y/x)で-π~πの値
sin(x)sin関数
cos(x)cos関数
exp(x)exp関数
log(x)自然対数
sqrt(x)平方根
int(x)小数点以下を切り捨て
rand()疑似乱数 0以上1未満
srand()乱数の初期化
Page Top

書式変換 anchor.png

printfとsprintfの中では次のような変換が利用できる。

記法
printf("|%c|",65)A
printf("|%d|",65)65
printf("|%5d|",65) 65
printf("|%05d|",65)00065
printf("|%f|",65)65.000000
printf("|%5.1f|",65) 65.0
printf("|%e|",65)6.500000e+01
printf("|%5.1e|",65)6.5e+01
printf("|%g|",65)65
printf("|%o|",65)101
printf("|%s|","yuji")yuji
printf("|%10s|","yuji") yuji
printf("|%-10s|","yuji")yuji 
printf("|%.4s|","yujiueno")yuji
printf("|%10.4s|","yujiueno") yuji
printf("|%-10.4s|","yuji")yuji 
printf("|%%|")%
Page Top

正規表現 anchor.png

正規表現の記法を示す。

記法
AAそのもの(以下の特殊文字を除く普通の文字)
\\\そのもの
\""そのもの
\tタブ
\n改行
\fフォームフィード
\bバックスペース
\0338進数033
^a文字列の先頭が'a'
a$文字列の末尾が'a'
.任意の1文字
[abc]'abc'のどれか1文字
[^abc]'abc'以外の1文字
[a-e]'abcde'のどれか1文字
[^a-e]'abcde'以外の1文字
a*0個以上の'a'の並び
a+1個以上の'a'の並び
a?'a'が1個あるいは0個
abc|de"abc"または"de"

*1 開発者のAhoさん,Weinbergerさん,Kernighanさんの頭文字から名前付けられたらしい。

新しくコメントをつける

題名
ゲスト名
投稿本文
より詳細なコメント入力フォームへ

トップ   凍結 差分 バックアップ 複製 名前変更 リロード   ページ新規作成 全ページ一覧 単語検索 最新ページの一覧   ヘルプ   最新ページのRSS 1.0 最新ページのRSS 2.0 最新ページのRSS Atom
Counter: 598, today: 1, yesterday: 0
最終更新: 2020-12-26 (土) 16:08:10 (JST) (1188d) by yuji