式はawk
のアクションの基本的な構成要素である。式を評価して得た値を出力
することもできるし、テストしたり、変数に格納したり関数に渡すこともできる。そ
れに加えて、代入文を使うことによって新たな値を変数やフィールドに代入すること
もできる。
式は文としても扱われる。大部分の文はオペランドとなるデータを特定する一つ以上
の式からなる。他の多くの言語と同様に、awk
における式は変数や、配列の参
照、定数、関数呼び出し、そしてそれらの演算子による組み合わせも含まれる。
もっとも単純な式は定数である。これは常に同じ値を持つ。定数には三種類あ り、数値定数、文字列定数、正規表現定数である。
数値定数は数を意味する。この数は整数でも良いし、小数でも良いし 科学(指数)表記の数であっても良い (8)。 以下に数値定数の例をいくつか挙げる。これらはすべて同じ値を持つ。
105 1.05e+2 1050e-1
文字列定数は二重引用符で囲まれたキャラクタの並びから構成される。 たとえば、
"parrot"
これは`parrot'という構成の文字列である。gawk
における文字列は、
任意の長さの、ASCIIのNUL(キャラクタコード 0)も含めて任意の8bit ASCIIキャ
ラクターを使うことが可能である。他のawk
処理系では一部のキャラクタ
コードを扱うことが難しいかもしれない。
訳注:日本語対応したjgawkでは、16bit長の漢字コードを 文字列の要素として使うことができる。
正規表現定数は/^beginning and end$/
のようにスラッシュで囲まれて
記述されている正規表現である。awk
で使われている大部分の正規表現は
定数であるが、`~' や `!~'といったマッチング演算子は"動的な"
正規表現(正規表現を含む普通の文字列や、変数)に使うこともできる。
`~' や `!~'といった演算子の右側で使われたとき、 正規表現定数は単にマッチングを試みる正規表現を意味するだけである。
正規表現定数(/foo/
)は単純な式のように扱われるかもしれない。
正規表現定数がそれ自身として現れたとき、それは
パターン中に現れたかのように、つまり`($0 ~ /foo/)' のように
みなされる。(d.c.)
(セクション パターンとしての式を参照).
これにより、次のような二つのプログラム
if ($0 ~ /barfly/ || $0 ~ /camelot/) print "found"
と
if (/barfly/ || /camelot/) print "found"
は、意味的にはまったく同じなのである。
この規則の一つの奇妙な結果とは、 次の正当なブール式はが、ユーザーが期待する結果とはならない というものである。
# note that /foo/ is on the left of the ~ if (/foo/ ~ $1) print "found foo"
このコードは"明らかに"$1
と/foo/
という正規表現とのマッチング
を行なうように見える。しかし実際にはこの(/foo/ ~ $1)
という式は、
(($0 ~ /foo/) ~ $1)
と同じである。言い換えるならば、最初に入力レコー
ドに対して /foo/
という正規表現を適用する。その結果は0か1のどちらかで
あり、マッチの成否によって決定する。その結果に対してレコードの第一フィールド
を適用してマッチングを行なう。
こういった動作は本当にユーザーが望んでいたテストとは違ったものであろうから、
こういった文をプログラム中で見つけると、gawk
は警告を発する。
もうひとつ、この規則による結果は代入文でも起きる。
matches = /foo/
この例では変数matches
に、カレント入力レコードの内容によって0か1のどち
らかが代入される。
言語のこの機能は、POSIX標準までドキュメント化されなかった。
正規表現定数は
gensub
, sub
, gsub
といった関数の
第一引数として使用され、また、match
関数の第二引数と
して使われる
(セクション Built-in Functions for String Manipulationを参照)。
gawk
も含めて
最近のawk
処理系では、split
関数の
第三引数として正規表現定数を許している。
一部の古い処理系ではそうではない(d.c.)。
これは、ユーザー定義関数 (セクション ユーザー定義関数を参照) の引数として正規表現定数を使おうとしたときに混乱を引き起こす。 例を挙げると、
function mysub(pat, repl, str, global) { if (global) gsub(pat, repl, str) else sub(pat, repl, str) return str } { ... text = "hi! hi yourself!" mysub(/hi/, "howdy", text, 1) ... }
この例では、プログラムはユーザー定義関数mysub
に
それを通してsub
や gsub
に渡される
正規表現定数を渡そうとしている。
しかしながら、実際の動作ではpat
パラメータは1か0になり、
それは/hi/
が$0
とマッチするかどうかに依存する。
これはおそらくユーザーが本当に渡そうとした値とは異なったものであるから、
gawk
はユーザー定義関数に対するパラメータに正規表現定数を
使おうしたときに警告を発する。
変数とはプログラムのある箇所で、後から別の箇所で参照するために値を格納す
る方法である。ユーザーは変数をプログラムのテキストの中で自由に扱うことが
でき、また、変数に対する値の代入をawk
のコマンドラインで行うことが
できる。
変数にはあとでその値を参照するために名前が付けられている。これまでにも多くの
例で変数が出てきた。変数の名前は文字、数字、あるいはアンダースコアの並びでな
ければならず、かつ、先頭が数字であってはいけない。変数名の大小文字は区別さ
れ、したがってa
と A
は別の変数である。
変数名はそれ自身が、変数の現在の値を表わす一つの正しい式である。 変数は代入演算子や増加演算子によって新たな値を与えられる セクション 代入式を参照.
一部の変数はあらかじめ特別な意味を持っているものがある。フィールドセパレータ
としてのFS
やカレント入力レコード中のフィールド数を表わすNF
など
がそれである。 セクション 組み込み変数を参照.にそういった変数のリスト
がある。これら組み込み変数は他の変数のように代入したり値を参照したりできるが、
その値は自動的にawk
が変更したり、使ったりする。組み込み変数の名前はそ
れぞれ大文字だけからなる。
awk
での変数は数値でも文字列でも代入することができる。デフォルトでは文
字列は空文字列で初期化され、それが数値に変換されるときには 0として扱われる。
このことは、awk
ではCやその他の多くの言語のように変数の初期化を陽に
行う必要がないということである。
awk
を起動したときに、コマンドライン上の引数にvariable assignment
を含めることによって、awk
の変数に値をセットすることができる
(セクション Other Command Line Argumentsを参照)。
代入は次のような形式である。
variable=text
こうすることによって、変数に対する値のセットをawk
の起動時か
入力ファイルと入力ファイルの間で行なうことができる。
ここで、代入の前に`-v'というオプションを次のように付けた場合、
-v variable=text
変数に対する値のセットは一番最初、BEGIN
ルールが実行される前に行なわれ
る。`-v'オプションとその後の代入はすべてのファイル名の引数の前になけれ
ばならず、またプログラムテキストがコマンドラインで与えられている場合にも、
それの前になければならない)。
そうでない場合には、変数に対する代入は前に位置しているファイル名を示す 引数が処理された後で行なわれる。たとえば、
awk '{ print $n }' n=4 inventory-shipped n=2 BBS-list
この例は全ての入力レコードのフィールド番号がn
のフィールドを出力する。
最初のファイルが読まれる前に、コマンドラインで変数n
に4がセットされる。
これによって、`inventory-shipped'から入力されるレコードの 4番目のフィー
ルドが出力される。最初のファイルの処理が終ったあと、そして二番目のファイルの
処理が始まる前に、n
は2にセットされる。したがって、`BBS-list'のレ
コードの二番目のフィールドが出力される。
$ awk '{ print $n }' n=4 inventory-shipped n=2 BBS-list -| 15 -| 24 ... -| 555-5553 -| 555-3412 ...
コマンドライン引数はawk
プログラム中でARGV
という名前の
配列によって参照することができる
(セクション Using ARGC
and ARGV
を参照)。
awk
processes the values of command line assignments for escape
sequences (d.c.) (セクション エスケープシーケンスを参照).
awk
プログラムの文脈により、必要に応じて文字列を数値に変換したり数値を
文字列に変換したりする。例えば、foo + bar
という式があったときに、
foo
か bar
の値(もしくは両方)が文字列であった場合、その値は加
算が行われる前に、数値に変換される。逆に文字列連結中で数値があった場合にはそ
れは文字列に変換される。例を挙げると、
two = 2; three = 3 print (two three) + 4
この例は数値の27を出力する。変数 two
と three
の数としての値は
文字列に変換され、それが連結される。その結果得られた文字列は再度 23という数
に変換され、それに4が加えられる。
何かの理由で数を強制的に文字列に変換したいときは、空文字列""
を
くっつける。逆に文字列を強制的に数に変換するには、文字列に0を加える。
文字列の頭の方に数字列がついているような文字列は、文字列から数値に変換される。
"2.5"
は2.5に、"1e3"
は1000に変換され、"25fix"
は数の値と
して25を持つ。正しい数として認識できない文字列は0に変換される。
数値の文字列への変換のルールは、awk
の組み込み変数CONVFMT
(セクション 組み込み変数を参照)によって制御される。
数値は関数sprintf
(セクション 組み込み関数を参照)
で書式指定文字列にCONVFMT
が渡されたのと同じ様に変換される。
CONVFMT
のデフォルトの値は"%.6g"
であり、これは値を最低6文字ある
として変換する。一部のアプリケーションのために、この精度を変更したいと考える
かも知れない。最近のマシンの大部分では倍精度実数は十進数で16から17桁の精度があ
る。
CONVFMT
に、sprintf
で通常の浮動小数点数を指定しているような文
字列ではないものをセットした場合、おかしな結果となるだろう。例えば書式文字列
の`%'を忘れた場合には、全ての数値は同じ文字列定数に変換される。
特別な場合として数値が整数であった場合、変換した結果の文字列は 常に
整数のものとなり、CONVFMT
の値に左右されない。次のようなプログラムの断
片があったとすると、
CONVFMT = "%2.2f" a = 12 b = a ""
b
has the value "12"
, not "12.00"
(d.c.).
b
は"12.00"
ではなく、"12"
という値を持つ(d.c.)。
POSIX の標準以前には、awk
は数値から文字列への変換の為に OFMT
の
値を使用していた。 OFMT
はprint
文で数値を出力するときの書式を指
定する。 CONVFMT
は、出力するときと、変換するときを区別するために導入
された。CONVFMT
とOFMT
の両方とも、デフォルトの値は"%.6g"
である。多くの場合、古いawk
はその挙動を変えないだろう。しかし、この様
なOFMT
の使用は、プログラムを他のインプリメントによる awk
処理系
に持っていくような場合には、常にそのことに注意せねばならない。こういった場合、
プログラムを直すよりもgawk
それ自体を移植することを勧める。
print
文に関する詳しい説明はセクション The print
Statementを参照。
awk
言語は、式を評価するときに一般的な算術演算子を使用する、これらの
算術演算子は通常の優先順位の規則にしたがっており、あなたの期待通りに働くだ
ろう。
これは、学生の名前と、各学生の三つのテストの点数が 記録されたリストからなる`grades'というファイルの内容である (小さなクラスだ)。
Pat 100 97 58 Sandy 84 72 93 Chris 72 92 89
次のプログラムに`grades'を入力ファイルとして与えると、 平均点を出力する。
$ awk '{ sum = $2 + $3 + $4 ; avg = sum / 3 > print $1, avg }' grades -| Pat 85 -| Sandy 83 -| Chris 84.3333
以下のリストはawk
の算術演算子一覧である。優先順位の高い順から
低い順にならんでいる。
- x
+ x
x ^ y
x ** y
2 ^ 3
の値は8である。 `**'という
キャラクタの並びは`^'と同じ働きをする (POSIXの標準ではべき乗に
`^'だけが使える)。
x * y
x / y
awk
のすべての数値は実数であるので、`3 / 4'
の結果は整数に丸められることはなく、0.75となる。
x % y
b * int(a / b) + (a % b) == aあるいは望ましくないであろう、この剰余の定義が起こす効果は xが負であっ たときには
x % y
の結果も負になるということである。
したがって、
-17 % 8 = -1となる。 他の
awk
処理系では、剰余の符号は実行しているマシンに依存する。
x + y
x - y
移植性を最大限に保つために、`**'演算子を使わないこと。
単項のプラスとマイナスは同じ優先順位であり、 乗除演算子はすべて同じ優先順位を持つ。また、 加算と減算は同じ優先順位である。
It seemed like a good idea at the time. Brian Kernighan
文字列の演算子はただ一つ、連接のみである。これはそれを表わす特定の 演算子を持たない。その代わり、ある式を別の式に何の演算子も 挟まずに連続して置いたときに連接が行われる。例を挙げよう。
$ awk '{ print "Field number one: " $1 }' BBS-list -| Field number one: aardvark -| Field number one: alpo-net ...
ここで、次のように`:'のあとにスペースがない文字列にしても同様である。
$ awk '{ print "Field number one:" $1 }' BBS-list -| Field number one:aardvark -| Field number one:alpo-net ...
文字列の連接が明確な演算子を持っていないので、連接を行なうアイテムをカッコで
囲んで連接が確実に行なわれることを保証することがしばしば必要となる。たとえば
次のようなコードではfile
と name
を連接するということが明確になっ
ていない。
file = "file" name = "name" print "something meaningful" > file name
これは次のように書く必要がある。
print "something meaningful" > (file name)
連接を使うときには、いつでもカッコで連接の全体を囲むことを勧める (とくに`='の右辺のオペランドであったりしたときには)。
代入はある変数に新しい値をセットする式である。
次の例では、z
という変数に1を代入している。
z = 1
この式が実行された後では変数z
の値は1になり、代入前のz
の値は失
われる。
文字列も同じ様に代入を行うことができる。例えば、次の例では message
と
いう変数に"this food is good"
という値が格納される。
thing = "food" predicate = "good" message = "this " thing " is " predicate
(この例はまた、文字列の連接も説明している)
`='は代入演算子と呼ばれる演算子の中でもっとも単純なもので、右辺の 値をそのまま変更せずに代入する。
大部分の演算子(加算、連接、など)は、値を計算する以外になんの作用も持たない。 もし、(計算された)その値を無視するならば、それは演算子を使わなかったのと同 じ結果となるだろう。しかし代入演算子は異なっていて、値を生成しその値を無視 したとしても代入操作は変更された値を自分に格納する。これは副作用と 呼ばれる。
代入の左辺は変数(セクション Variablesを参照)を必ずしも必要とせず、それはフィールド
(セクション フィールドの内容を変更するを参照) や配列の要素
(セクション awk
における配列を参照)であってもよい。これらは総称して
左辺値と呼ばれ、代入演算子の左辺に置くことができる。右辺にはどのような
式、特定の値を代入する式やフィールド、配列要素でも
(右辺値と呼ばれるような値を)、置くことができる
重要なことは、変数は常に同じ型であるというわけではないという事である。変数の
型は単純に、その時点で格納している値によって決定される。次に挙げるプログラム
の断片では、変数foo
は最初は数の値を持ち、その後は文字列値を持っている。
foo = 1 print foo foo = "bar" print foo
二番目の代入文でfoo
に文字列値を与えたときに、それ以前の数の値は失われ
る。
数字で始まっていない文字列の値は数値(numeric value)として
はゼロである。次のコードを実行すると、foo
の値は5となる。
foo = "a string" foo = foo + 5
(変数を数値として扱い、その後で文字列として扱うことは混乱を招きやすく、
よくないプログラミングスタイルであることに注意すること。先の例は
awk
の動作がどんなものかを説明するものであって、
このようにプログラムを書けば良いという方法ではない!)
代入はそれ自身の値(代入された値)を持つ式である。従って、z = 1
は1と
いう値を持つ式である。この結果、多重代入を記述することもできる。
x = y = z = 0
この例では三つの変数全てに、0を格納する。なぜなら、z = 0
の値は0であり、
それがy
に格納され、さらにy = z = 0
の値、つまり0がx
に格
納される。
代入は式のどこででも使うことができる。例えばx != (y = 1)
という記述は
正しく、これはy
に1をセットし、それからx
が1と等しいかどうかを
テストする。しかし、このスタイルはプログラムを読みにくくするものである。
one-shot programを除いては、このようなネストした代入を取り除くように書きなお
すべきである。
`='は別として、その他の代入演算子は変数の(代入前の)古い値と演算を行う。
例えば、`+='という演算子は右辺値を変数の古い値と加算して新しい値を計算
する。従って、次の例はfoo
の値に5加えるということになる。
foo += 5
これは次のように記述したものと同じである。
foo = foo + 5
`+='という演算子を使ったことによりプログラムの意味が明確になる。
`+='(又は任意の代入演算子)を使ったときと、 演算子の左側のオペランドを演算子の右側で繰り返し使ったときとで 異なる場合がある。例を挙げよう。
# Pat Rankin に感謝 BEGIN { foo[rand()] += 5 for (x in foo) print x, foo[x] bar[rand()] = bar[rand()] + 5 for (x in bar) print x, bar[x] }
rand
は呼ばれるたびに異なる値を返すので、bar
の
添え字は異なることが保証される
(配列とrand
関数はまだ説明していない。詳しくは
セクション awk
における配列を参照,
とセクション Numeric Built-in Functionsを参照)。
同様に、式が評価されるときに左側を最初にするのか、右を最初に するのかは処理系次第である。そのため、
i = 1 a[i += 2] = i + 1
この例のa[3]
の値は2にも4にもなりうる。
以下は算術演算子の一覧である。これらの演算子は右辺のオペランドを数値 に変換する。
lvalue += increment
lvalue -= decrement
lvalue *= coefficient
lvalue /= divisor
lvalue %= modulus
lvalue ^= power
lvalue **= power
^=
という演算子だけが POSIX で定義されている)
移植性を最大限に保つために、`**='演算子は使わないようにすること。
インクリメント演算子やデクリメント演算子は変数の値を1増や
すか、1減らすかする。この演算子は代入と同時に使うこともでき、 インクリメ
ント演算子はそういった場合にawk
言語に対して何の影響も及ぼさないが、
非常によく行なわれることを省略して記述することができる。
1を加える演算子は`++'と記述する。この演算子は変数のインクリメントを、 その値を得る前、得た後のどちらでも行える。
vに対して先にインクリメントを行うには++v
と記述する。これ
はvの値に1を加えて、新しい値がこの式の値となる。 v += 1
と
いう代入式はこれと全く同じである。
変数の後に`++'を記述したものはポストインクリメントと呼ばれる。これは変
数の値をインクリメントするが、先ほどと違って、インクリメント式自体の値は変数
の古い値である。従って、foo
の値が4のとき、foo++
という式の値は4
であるが foo
の値は5に変更されている。
ポストインクリメントのfoo++
は(foo+= 1) - 1
という記述とほぼ
同じであるが、完全に同じというわけではない。それは、awk
での数値は
全て浮動小数点数だからである。浮動小数点数では、 foo + 1 - 1
は
code{foo}と同一ではない。しかし、かなり小さな数(10の12乗、つまり一兆未
満)しか扱わない限りはこういった違いはごく僅かである。
あらゆる左辺値はインクリメントすることができる。フィールド、及び配列要素は変 数と同じ様にインクリメントが行われる(フィールドの参照と、変数のインクリメ ントを同時に行いたいときには `$(i++)'を使う。この括弧はフィールド参照演 算子`$'の優先順位の為に必ず付けなければならない)。
デクリメント演算子`--'は`++'と同じ様に動作するが、加算ではなく、1 を減算する。`++'と同じく、左辺値の前に置いてプリデクリメントで使用した り、左辺値の後ろに置いてポストデクリメントで使用することができる。
次にインクリメント式、デクリメント式の簡単な説明をする。
++lvalue
lvalue++
--lvalue
++lvalue
と似ているが、加算ではなく減算を行う。
++lvalue
を減じ、その結果が式の値となる。
lvalue--
lvalue++
と似ているが加算ではなく減算を行う。
lvalueを減じ、式の値はデクリメントする前の古い値となる。
awk
多くのプログラミング言語では、"真"、"偽"を表わすような特別なものがある。
そういったものを持ったプログラミング言語では、true
とかfalse
あるいはこれらの大文字のものを一般に使っている、
しかしawk
は違っている。awk
では、非常に単純な真、偽に関する
コンセプトをCから受け継いでおり、0ではない数値、あるいは空ではない文字列は
真となる。そうではない値(ゼロか空文字列、""
)は偽となる。
次のサンプルプログラムは、`A strange truth value'を三回出力する。
BEGIN { if (3.1415927) print "A strange truth value" if ("Four Score And Seven Years Ago") print "A strange truth value" if (j = 57) print "A strange truth value" }
びっくりするような"non-zero or non-null" の結果が生じることがある。
"0"
という文字列定数は、"真"である。なぜなら、これは
空文字列ではないからである(d.c.)。
The Guide is definitive. Reality is frequently inaccurate. The Hitchhiker's Guide to the Galaxy
他のプログラミング言語とは異なり、awk
の変数は固定した型を持ってお
らず、なにから代入されたかによって数値にも文字列にもなる
1992年のPOSIX標準では、数値文字列(numeric string)の概念が
導入された。これは" +2"
のような、数値のように見える文字列である。
この概念は変数の型を決定するのに使われる。
変数の型は二つの変数をどのように比較するのかを決定するのに影響するので、 非常に重要である。
gawk
では、変数の型は以下のルールに従う。
getline
入力、FILENAME
、ARGV
の要素、
ENVIRON
の要素、split
で作成された配列、といったもので
数値文字列のものはstrnum属性を持っている。
それ以外(数値文字列でないもの)はstring属性を持つ。
初期化されていない変数は同様にstrnumを持つ。
この最後のルールは特に重要である。次のプログラムでは、a
は、文字列演
算の後でも数値タイプを持つ。
BEGIN { a = 12.345 b = a " is a cute number" print b }
二つのオペランドを比較するときは、オペランドの属性によって文字列演算子か 数値演算子のいずれかが以下の一覧に従って決定され、使用される。
基本的な考え方は、ユーザーの入力が数値のように見え、ユーザーが 入力だけ行ったときには、たとえそのデータがキャラクタから構成され ていて、文字列であったとしても数値として扱う。
比較式(comparison expression)は文字列あるいは数値の、等しいかな どの比較を行う。この式はCのスーパーセットになっている関係演算子 (relational operators)を使用する。以下に一覧にする。
x < y
x <= y
x > y
x >= y
x == y
x != y
x ~ y
x !~ y
subscript in array
比較式はその式が真のときには1、偽のときは0という値を持つ。
(セクション Conversion of Strings and Numbersを参照).
異なるタイプを混ぜで比較を行うとき、数値のオペランドは
CONVFMT
の値を使って文字列に変換される
(セクション Conversion of Strings and Numbersを参照)。
文字列の比較は文字列の先頭からキャラクタごとの比較で行なわれる。した
がって、 "10"
は "9"
よりも小さい、ということになる。二つ
の文字列が他方の前の部分に等しいような場合、つまり "abc"
と
"abcd"
の比較では、短い方の文字列 "abc"
がより小さい、と認
識される。
次の例は、非常にありがちな、`=='演算子の`='を一つ忘れてしまう
というものである。これは正しいawk
プログラムであるが、期待した動
作はしないだろう。
if (a = b) # おっと、ここは a==bであるべきだ ... else ...
b
が0か、空文字列でない限り、if
の条件式は常に真になる。演算
子がこのように似ているために、この種のエラーはソースコードを眺めたときに
見つけることは非常に難しい。
以下にgawk
がどのように比較を行ない、どのような結果となるのかの
例をいくつか挙げる。
1.5 <= 2.0
"abc" >= "xyz"
1.5 != " +2"
"1e2" < "3"
a = 2; b = "2"
a == b
a = 2; b = " +2"
a == b
$ echo 1e2 3 | awk '{ print ($1 < $2) ? "true" : "false" }' -| false
この例では`false'が出力される。なぜなら$1
と $2
の両方が
数値文字列(strnum)であり、文字列でもあり数値でもあるので数値として比較が
行なわれたからである。
比較ルールと数値文字列を使用する目的は、正しい動作を、驚くようなことができる だけ少なくなるように行なわせるということである。
文字列の比較と正規表現の比較は非常に異なっている。例を挙げると、
x == "foo"
この式は変数x
が`foo'であるときに 1あるいは真の値を持つ。対
照的に、
x ~ /foo/
この式はx
に`foo'が含まれているもの、例えば
"Oh, what a fool am I!"
であれば式の値は1となる。
`~' 演算子や `!~'演算子の右側にくるオペランドは
正規表現定数(/.../
)でもあり、通常の式でもある。
これは、ちょうど文字列としての式の値が動的正規表現
(セクション 正規表現の使い方を参照、
セクション 動的正規表現を使うを参照)
であるということと同じである
最近のawk
の処理系では、スラッシュで囲まれた正規表現定数はそれ自体が
式であり、/regexp/
という正規表現は比較式の省略形である。
$0 ~ /regexp/
/foo/
が`$0 ~ /foo/' の省略形にならない
特別な場所は、`~'演算子や`!~'の右辺のオペランドである。
詳しくは
セクション 正規表現定数を使うを参照.
で述べられている。
ブール式は比較式やマッチング式を論理演算子(`||')や (`&&')、 (`!')を用いて繋げたものである。論理式全体の真偽は各部分式の真偽値の組み 合わせによって決定される。ブール式は論理式でもあり、これら二つの 単語の意味は同じである。
ブール式は、比較式やマッチング式を記述できるところであればどこでも記述すること
ができるし、if
、while
、do
や for
といった文のな
かで使うこともできる
(セクション アクション中の制御文を参照)。
ブール式は真のとき1、偽のとき0の値を持ち、その結果を変数
に格納したり、算術式に使うこともできる。
それに加えて、それぞれのブール式は正しい論理パターンでもあるので、 ルールの実行を制御するパターンとして使うこともできる。
以下に三つのブール演算子と、それを使った例を挙げる。
boolean1 && boolean2
if ($0 ~ /2400/ && $0 ~ /foo/) print部分式boolean2は、boolean1が真であったときにのみ評価が行なわ れる。このことによって、boolean2が副作用を持つ式であったときに違っ た結果になるようにすることもできる。たとえば
$0 ~ /foo/ & ($2 == bar++)
といった式では、`foo'がレコード中に
存在しなかった場合には変数bar
はインクリメントされない。
boolean1 || boolean2
if ($0 ~ /2400/ || $0 ~ /foo/) print部分式boolean2は、boolean1が偽であったときにだけ評価が行なわれる。 このことにより、boolean2が副作用を持っていたときには異なった結果をもた らすこともある。
! boolean
awk '{ if (! ($0 ~ /foo/)) print }' BBS-list
`&&'演算子や`||'演算子は、その動作から短絡評価 (short-circuit)演算子と呼ばれる。一部分を評価することで全体の 値を決定できる場合には、式全体の評価は"短絡"(short-circuited)される。
`&&' や `||'の後に改行を置くことによって、
行の継続をすることができる。しかし、改行をこれらの演算子の
前に、バックスラッシュによる行継続なしで置くことはできない
(セクション awk
の文と行を参照)。
`!'演算子を使った式の値は1か0のいずれかになる。これは 演算子を適用した式の真偽の値による。
`!'演算子はフラグ変数の意味を 真から偽に変えたり、逆方向に変えるのによく使われる。 例えば、次のプログラムは特殊なパターン行の間の行を出力するものである。
$1 == "START" { interested = ! interested } interested == 1 { print } $1 == "END" { interested = ! interested }
interested
という変数は、awk
のすべての変数と同様に
初期値0(偽)でスタートする。最初のフィールドが`START'である
ような行を見つけたとき、interested
の値は
`!'を使って真へと変わる。最初のフィールドが`END'で
あるような行を見つけたとき、interested
は偽へと戻る。
条件式は三つのオペランドをとる特殊な式である。この式は、ある一つの式の 値によって二つの異なった式のうちどちらか一つを選択するのに使用できる。
条件式はCのそれと同じ様に次のような形で記述される。
selector ? if-true-exp : if-false-exp
三つの部分式の内、最初のselectorは常に最初に計算され、その値が"真" (0でなく、nullでもない)であれば、次に if-true-expを計算し、その値が 式全体の値となる。 "真"でなかった場合には、次にif-false-expを計算し、 その値が式全体の値となる。
例えば、次の式はx
の値の絶対値を作る。
x > 0 ? x : -x
条件式が計算される毎に、if-true-exp か if-false-expのどちらか一
つだけが計算され、もう一つは無視される。このことは、式が副作用を持っていると
きには重要なことである。例えば、次に挙げる条件式は配列a
もしくはb
の添字i
の要素を取りだし、i
をインクリメントする。
x == y ? a[i++] : b[i++]
この例ではi
のインクリメントを一回しか行わない。それは、この式全体が実
行される度に、二つのインクリメント式のうちどちらか一つが実行され、もう片方は
実行されないからである。
配列に関する詳しい説明はセクション awk
における配列を参照。
gawk
のちょっとした拡張として、`?:'を使って、
そのいずれかのキャラクタの直後に改行を置くことによって
文の継続を行うことができる。しかし、これらのキャラクタの
前に改行を、バックスラッシュによる継続抜きで
置くことはできない
(セクション awk
の文と行を参照)。
`--posix'が指定されている場合
(セクション コマンドラインオプションを参照)、この拡張は抑制される。
関数とは計算を特定する名前である。名前があるので、プログラム中の任意の
場所で呼び出すことができる。例えば、sqrt
関数は、数値の平方根を計算す
る。
ある固有の関数群は組み込みであり、全てのawk
プログラムで使うこと
ができる。 sqrt
関数はそのうちの一つである。
セクション 組み込み関数を参照.に組み込み関数のリストと、関数の説明があ
る。組み込み関数に加え、プログラムのどこかで使うためにユーザーが関数を定義す
ることができる。これについては、
セクション ユーザー定義関数を参照。
関数を使うには、関数名とそれに続く括弧で囲まれた引数リストからなる 関数 呼び出し式によって行う。引数は関数が計算を行う為に与えられる生のデータであ る。二個以上の引数がある場合には、引数の間にカンマを置く。引数がない場合には `()'を関数名に続けて書く。例を挙げる。
sqrt(x^2 + y^2) 引数一つ atan2(y, x) 引数二つ rand() 引数なし
関数名と、開き括弧の間には空白を置いてはいけない! ユーザー定義関数 の名前は変数名と同じ様に見え、空白は変数と括弧の中の式との連結のように見せて しまう。括弧の前の空白は組み込み関数には無害なものであるが、ユーザー定義関数 を使用する時に、間違って余計な空白を入れないようにするには、常に関数名と開き 括弧の間を空けないようにするのが最良の方法だろう。
個々の関数にはその関数固有の数の引数がある。例えば、sqrt
関数は、呼び
出し時に必ず平方根を計算するための引数がなければならない。
sqrt(argument)
一部の組み込み関数では最後の引数を省略することができ、省略した場合には適当な デフォルトの値が使われる。詳しくはセクション 組み込み関数を参照.を参照。 ユーザー定義関数の呼び出しで引数を省略した場合、そのような引数はローカル変数 として扱われ、空文字列に初期化される (セクション ユーザー定義関数を参照)。
他の式と同様に関数呼び出しも値を持ち、その値は与えた引数に応じて関数が計算し
た結果である。例えば、sqrt(argument)
の値は与えた引数の平方根で
ある。関数は変数への代入や入出力の実行のような副作用を持つことができる。
次の例では、一行当たり一つの数値データを読み取り、読み取った値と、その平方根 を一行に出力する。
$ awk '{ print "The square root of", $1, "is", sqrt($1) }' 1 -| The square root of 1 is 1 3 -| The square root of 3 is 1.73205 5 -| The square root of 5 is 2.23607 Control-d
演算子優先順位は一つの式の中で異なった演算子があったときに、どのように
演算子をまとめるかを決定する。例えば、`*' は `+'よりも優先順位が高
く、従ってa + b * c
は b
と c
を掛けて、それにa
を
加える。つまり、 a + (b * c)
の様に解釈される。
演算子の優先順位は、括弧を使うことによって無視することができる。優先順位規則 は括弧を書いた通りであると考えることができる。事実、通常使われないような演算 子の結合があるときに、常に括弧を使うのは賢いやり方である。なぜなら、他の人が プログラムを読んだときに、その場合の優先順位を思い出せなかったりあるいは自分 自身が忘れてしまったりするからである。陽に括弧を使うということは、このような ミスをしないような助けになる。
優先順位が同じ演算子を一緒に使った場合、代入、条件式、そしてべき乗演算子を除
けば左側からまとめられる。従って、a - b + c
は(a - b) + c
に、
a = b = c
はa = (b = c)
に解釈される。
単項で前置される演算子の優先順位は、それ単独で使うのと同じ様に、一番深いとこ
ろが最初に解析されるので、あまり意味のないことである。それはただ一通り、にし
か解釈できないからである。従って、$++i
は $(++i)
と解釈されるし、
++$x
は++($x)
と解釈される。しかし、オペランドに他の演算子が続
いているような場合には、単項演算子の優先順位が意味を持ってくる。つまり、
$x^2
は ($x)^2
であるが、-x^2
は-(x^2)
であるとい
うことである。これは、`-'の優先順位が`^'よりも低く、 `$'が全
体で最も高い優先順位であるからである。
以下にawk
の演算子を優先順位の高いものから並べる。
(...)
$
++ --
^ **
+ - !
* / %
+ -
Concatenation
< <= == !=
> >= >> |
print
文やprintf
文中の入出力リダイレクション演算子は、式では
なく文のレベルに属している。リダイレクションは他の演算子のオペランドとな
る様な式を生成しない。その結果、リダイレクションをより優先順位の低い演算
子のそばに括弧なしで記述するということは意味がなく、 例えば
`print foo > a ? b : c'はシンタックスエラーとなる。この文の正しい書き
方は`print foo > (a ? b : c)'である。
~ !~
in
&&
||
?:
= += -= *=
/= %= ^= **=