移動先 先頭, , , 末尾 セクション, 目次.

出力

もっともよく行われるアクションの一つは、入力の一部もしくは全部を出力するこ とである。単純な出力のためにprint文を使い、整形して出力するために printf文を使う。この両者はこの章で説明する。

print

print文は出力を単純に、標準的な書式で行う。カンマで区切られたリストに なっているデータを文字列とするか数値として出力するかを特定できるだけである。 出力は一つのスペースで区切られ、最後に改行が加えられる。例を挙げよう。

print item1, item2, ...

アイテムのリストは、全体を括弧でくくる事もできる。この括弧は、アイテムのいず れかが関係演算子を使った式である場合には使う必要がある。そうしないと、リダ イレクションと混同してしまう可能性があるだろう (セクション printprintfの出力のリダイレクトを参照). 関係演算子には `==', `!=', `<', `>', `>=', `<=', `~' そして `!~' がある。 (セクション 比較式を参照).

プリントの対象となるアイテムは文字列定数や数値定数、カレントレコードのフィー ルド($1のような)、変数、式である。 print文は出力する値を完全 に計算する。二つの例外として、どのように出力するか、つまりどの位カラムを出力 するか、指数表示を使うか使わないか等を指定することはできない (セクション 出力セパレータを参照, と セクション printを使った数値出力の制御を参照.) そういったときにはprintf 文を使う必要がある (セクション printf文を使った Fancier Printingを参照)。

単に`print' とだけ書かれて何もアイテムのない`print' 文は `print $0'と同じ動作、つまりカレントレコード全体を出力する。空行を出力 するには、`print ""'として、出力するコードにnullまたは空文字列を指定す る。

固定したテキストを出力するために、"Hello there"の様な文字列定数 を使う。ここで二重引用符を忘れた場合、そのテキストは式として扱われて、おそ らくはエラーとなるだろう。また、二つのアイテムの間にはスペースが出力される ことを覚えておいて欲しい。

ほとんどの場合、各々のprint文は一行の出力を作り出す。しかし、一行に 限らない。もし、アイテムの値が改行を含む文字列であった場合、改行は残りの文字 列とともに出力される。このやり方で、一つのprintで多くの行を出力するこ とができる。

print文の例

次に挙げるのは改行が埋め込まれた文字列の出力の例である。

awk 'BEGIN { print "line one\nline two\nline three" }'

出力はこうなる。

line one
line two
line three

次の例はカレントレコードの最初の二つのフィールドを空白で区切って出力する。

awk '{ print $1, $2 }' inventory-shipped

出力はこうなるだろう。

Jan 13
Feb 15
Mar 15
...

print 文を使うときにありがちな間違いは、二つのアイテムの間に置かれるカ ンマを忘れてしまうというものである。それはスペースなしに、一つにまとまって出力 される結果となるだろう。なぜなら、二つの並んだ文字列というのはawk で は連接を行う式を意味するからだ。カンマがない例として、

awk '{ print $1 $2 }' inventory-shipped

出力はこうなる。

Jan13
Feb15
Mar15
...

もう一つの例は、 `inventory-shipped'にあまり馴染みのない人に対してもわ かりやすい出力をする、というものである。最初のヘッダ行で何を出力しているか を明確にしようとして、月ごとの量($1)と、緑のクレーの積込量 ($2)のテーブルにヘッダを付け加えている。これをBEGINパターン (セクション スペシャルパターンBEGINENDを参照) を使って出力の最初に一回だけ出力させている。

awk 'BEGIN {  print "Month Crates"
              print "----- ------" }
           {  print $1, $2 }' inventory-shipped

どう出力されるか予想できるだろうか? このプログラムの出力はこうなる。

Month Crates
----- ------
Jan 13
Feb 15
Mar 15
...

ヘッダとデータのテーブルがきちんと揃っていない! しかし、二つのフィールドの 間に固定長のスペースを出力するようにできる。

awk 'BEGIN { print "Month Crates"
             print "----- ------" }
           { print $1, "     ", $2 }' inventory-shipped

このカラムの揃え方では、多くのカラムがあった場合に揃えるのが少々面倒になることが 想像できるだろう。二、三個のカラムのためなら単純にスペースを数えられるだろう が、これがもっと多かったりすると数え間違えやすくなる。それが printf 文が作られた理由である。 (セクション printf文を使った Fancier Printingを参照) その機能の一つにデータのカラムをそろえることがある。

出力セパレータ

先に述べたように、print文はカンマで区切られたアイテムのリストから構成 される。出力では、アイテムは普通に一つのスペースで区切られる。しかし、スペー スでなければならないというわけではなく、単に一つのスペースがデフォルトである、 ということである。組み込み変数のOFSに設定することによって、 出力 フィールドセパレータを任意の文字列にすることができる。この組み込み変数の初 期値は" "という文字列、つまりただ一つの空白である。

完全なprintプリント文の出力は、出力レコードと呼ばれる。個々の print文は一つの出力レコードを出力し、その後に出力レコードセパレータ と呼ばれる文字列を出力する。組み込み変数のORSはこの文字列を規定する。 この変数の初期値は改行キャラクタからなる"\n"という文字列である。従っ て、通常個々のprint文は行を分けるのである。

組み込み変数のOFSORSに新しい値を代入することによって、出力 フィールドや出力レコードをどのように区切るかを変更することができる。このよ うな操作は通常BEGINルールの中で行われる。通常これはBEGINルー ルの中で、入力が処理される前に行われる (セクション スペシャルパターンBEGINENDを参照)。 この動作を入力ファイル名の前に置かれたコマンドライン上での代入で行うことも できる。

次の例は入力レコードの一番目と二番目のフィールドをセミコロンで区切り、各行 の後に空行を加えて出力する。

awk 'BEGIN { OFS = ";"; ORS = "\n\n" }
           { print $1, $2 }'  BBS-list

ORSに改行が含まれていなかった場合、別の手段で陽に改行を 出力しない限り、全ての出力は一つの行にまとめて行われるだろう。

printを使った数値出力の制御

print 文を数値を出力するために使った場合、awkは内部で数値を (その数値を表す)文字列に変換し、その文字列を出力する。 awkはこの変 換動作のためにsprintf関数を使用する。現在のところ、sprintf関数 が数値(や文字列)をどのように書式化するかを記述されている 書式指定 を受諾し、数値の書式指定は違った方法が何種類もあるということ だけで十分である。書式の違いは セクション printf文を使った Fancier Printingを参照. で詳しく説明されている。

組み込み変数OFMTにはデフォルトの書式指定が格納されていて、出力すると きに数値を文字列に変換する必要があるときにprint文が sprintfと共 に使用する。 OFMTの値として異なった書式指定を与えることによって、 print文がどのように数値を出力するかを変更できる。簡単な例を挙げよう。

awk 'BEGIN { OFMT = "%d"  # 数値を整数として出力
             print 17.23 }'

この例では`17'が出力されるだろう。

printf文を使った Fancier Printing

printで出力するには複雑な書式を使いたいという場合には、printf を使うと良い。 printfを使うことによってアイテムごとにその幅を指定し たり、数値を出力するのにそれをどのようなスタイルで出力するのかを指定するこ とができるようになる(使用する基数や、符号を出力するかどうか、小数点以下何 桁まで出力するか)。他の引数をどのように、どこに出力するかを制御する 書式文字列と呼ばれる文字列によって、それを行なうことが可能である。

printf

printf 文は次のような形式である。

printf format, item1, item2, ...

リスト全体を括弧でくくる事もできる。括弧はアイテムである式が関係式を使ってい る場合には必要になる。括弧を使わないとリダイレクションと混乱するかもしれない (セクション printprintfの出力のリダイレクトを参照). 関係演算子には `==', `!=', `<', `>', `>=', `<=', `~', `!~' がある(セクション 比較式を参照).

printfprintの違いはformatという引数にある。この引数は 文字列として与えられ、他の引数をどのように出力するかという指定を行う。この ような引数は書式文字列と呼ばれる。

この書式文字列は ANSI Cライブラリのprintf関数のものと同じである。大 部分の書式は(書式文字列の)文字通りに出力されるテキストである。書式指定子 はアイテム一つ毎に一つの割合で、このテキスト中に置かれている。個々の書 式指定子は、次のアイテムをどのような書式で出力するかを指定している。

printfは自動的に改行を付け足すような動作はせず、書式文字列で指定さ れた通りに出力を行う。だから、改行を出力したい場合には、書式文字列に改行コー ドを含めなければならない。出力時のセパレータを指示する変数 OFSORSは、printf文の実行結果に対して何の影響も及ぼさない。

書式制御文字

書式指定子は`%' で始まり、(printf文でどのように出力するかを指定する) 書 式指定文字で終わる(`%'そのものを出力したい場合には `%%'と記述する)。 書式指定文字は値をどのように出力するかを指定する。書式指定子の残りは、どの位 のフィールド幅で出力するかというような事を指定する(省略可能な) 修飾子である。

以下は書式指定文字のリストである。

`c'
数値をASCIIキャラクタとみなして出力する。`printf "%c",65'`A'を 出力する。文字列を与えた場合は文字列の先頭にあるキャラクタを出力する。
`d'
十進整数で出力する。
`i'
これも十進整数で出力する。
`e'
指数形式で数値を出力する。例えば、
printf "%4.3e", 1950
小数点以下を含めて全部で四つの数字からなる `1.950e+03'を出力する。 `4.3' は 修飾子(modifiers)である(後述)。
`f'
浮動小数点形式で数値を表示する。
`g'
指数形式か浮動小数点形式の、いずれか出力するキャラクタ数が少なくなる 方で出力する。
`o'
八進の符号なし整数を出力する。
`s'
文字列を出力する。
`x'
十六進の符号なし整数を出力する。
`X'
十六進の符号なし整数を出力するが、10から15までの数値を表現するのに `a' か ら `f' ではなく、`A' から `F'の文字を使用する。
`%'
これは本当は書式制御文字ではない、しかし`%'の後に続けて書かれた場合には 意味がある。`%%'と並べて書いたときには一文字の`%'を出力し、引数 を取らない。

printf 書式の修飾子

書式指定には出力されるアイテムの値と、出力する大きさを制御するために 修飾子 を含めることができる。修飾子は`%'と書式指定文字の間に置かれる。 以下に使うことのできる修飾子を挙げる。

`-'
マイナス記号は出力幅の修飾に使い、与えられた出力幅の中で左詰めを行うことを 指示する。通常は与えられた出力幅の中で右詰めで表示がなされる。 従って、次のような指定をすると、
printf "%-4s", "foo"
`foo 'が出力される。
`width'
これはフィールドの幅を指定する数字である。 `%' と書式指定文字の間に置 かれ、この幅にフィールドを広げて出力を行うことを指定する。デフォルトではフィ ールドを埋めるためのスペースはフィールドの左から置かれる。 次のように指定した場合、
printf "%4s", "foo"
` foo'が出力される。 width は(出力の)最小幅であって、最大幅ではない。アイテムの値が width よりも多くのキャラクタを必要としたならば、出力はその幅となる。 したがって、
printf "%4s", "foobar"
この出力は `foobar'となる。 マイナス記号を伴ったフィールド幅指定は、幅あわせのためのスペースを フィールドの左ではなく右に置く。
`.prec'
これは、出力するときに精度を指定するのに使用する。 ここで指定する数字は小数点以下何桁まで出力したいか、という数字である。 対象が文字列であれば、その文字列を出力するときに最大幾つのキャラクタを

Cライブラリのprintfは実行時にwidthprecを指定すること (例えば"%*.*s"のように)ができる。書式文字列中でwidthprecを記述する代わりに、引数リストでそれを渡す事ができる。例えば

w = 5
p = 3
s = "abcdefg"
printf "<%*.*s>\n", w, p, s

これは次の書き方と等しい。

s = "abcdefg"
printf "<%5.3s>\n", s

上のプログラムは両方とも`<**abc>'を出力する(こ こでスペースを表すのに"*" というシンボルを使って、出力に二つのス ペースがあることがはっきりわかるようにしている)。

awk の初期のバージョンではこの機能がサポートされていなかった。次の様 に書式文字列を組み立てるのに文字列連接を使う事で、この機能をシミュレートす る事が可能である。

w = 5
p = 3
s = "abcdefg"
printf "<%" w "." p "s>\n", s

しかし、これは読み易いとは云えない。

printfを使った例

次の例はprintfを使ったテーブルを揃える方法である。

awk '{ printf "%-10s %s\n", $1, $2 }' BBS-list

ファイル`BBS-list' のBSSの名前($1)を十文字の長さで左寄せをして 出力する。そしてその後ろに電話番号($2)を出力する。この例は名前と電 話番号の二つのカラムを持つテーブルを桁揃えして出力する。

aardvark   555-5553
alpo-net   555-3412
barfly     555-7685
bites      555-1675
camelot    555-0542
core       555-2912
fooey      555-1234
foot       555-6699
macfoo     555-6480
sdace      555-3430
sabafoo    555-2127

なぜ電話番号を数値として出力するように指定していないのかわかるだろうか? ここ では数字がダッシュで区切られているので文字列として出力している。もし、これを 数値として出力しようとすると、ダッシュは減算の記号として解釈されてしまうだろ う。そうなると結果はおかしなものとなってしまうだろう。

ここで、電話番号は行の最後にあるので出力幅を指定していない。電話番号の後ろに スペースを出力する必要はないからだ。

先頭のカラムにヘッダを付け加えることによってテーブルをより見やすいものに している。これを行う為に、BEGINパターン (セクション スペシャルパターンBEGINENDを参照) を使ってヘッダをawkプログラムの始めで一度だけ出力している。

awk 'BEGIN { print "Name      Number"
             print "----      ------" }
     { printf "%-10s %s\n", $1, $2 }' BBS-list

この例ではprint文と printf文を混ぜて使っているが 気がついただろうか?、同じ結果をprintf文だけを使って得る事ができる。

awk 'BEGIN { printf "%-10s %s\n", "Name", "Number"
             printf "%-10s %s\n", "----", "------" }
     { printf "%-10s %s\n", $1, $2 }' BBS-list

ヘッダと同じ書式指定を使って各カラムを出力しているので、ヘッダと桁揃えされる。

三回同じ書式を指定するのに変数に書式を代入してその変数で指定する方法がある。 実例を挙げよう。

awk 'BEGIN { format = "%-10s %s\n"
             printf format, "Name", "Number"
             printf format, "----", "------" }
     { printf format, $1, $2 }' BBS-list

ためしに、前のprint文(セクション printを参照)の節 で説明した`inventory-shipped'の例の、表題と表のデータを printf文でそろえてみてほしい。

printprintfの出力のリダイレクト

これまでは出力は標準出力に対してのみ行われていた。 printprintfの両方とも別の場所に出力を送ることができる。 これはリダイレクトと呼ばれる。

リダイレクションはprint 文か printf文の後に書く事ができる。 awkにおけるリダイレクションはシェルコマンドのリダイレクションとよく似 ているが、awkではプログラムの中にリダイレクションの指示を書くという点 は違っている。

ファイルやパイプへの出力のリダイレクト

以下に、三種類の出力リダイレクションの例を挙げる。例ではprint文が使わ れているが、printf文を使っても同じように動作する。

print items > output-file
この書式のリダイレクションではアイテムの出力をoutput-fileという ファイルに対して行う。ファイル名output-fileは式であってもかまわない。 式を文字列に変換し、その後にファイル名として使用する (セクション アクションとしての式を参照). この書式のリダイレクションは最初の出力を行う前に、output-file 、ファイ ルの削除を実行する。以後はoutput-file の削除はしないでファイルに追加す る形で出力を行う。もしoutput-file が存在しなければファイルが新たに作成 される。 たとえば、次に挙げるプログラムはBBSの名前のリストを`name-list'という名 前のファイルに、電話番号のリストを`phone-list'というファイルにそれぞれ 出力する。出力されたファイルの各行は、一つの名前あるいは番号で構成される。
awk '{ print $2 > "phone-list"
       print $1 > "name-list" }' BBS-list
print items >> output-file
このリダイレクションは、出力をoutput-fileというファイルに対して行う。 先ほどの`>'での指定と違うのは、output-fileの削除が行われないとい うことである。出力はファイルに追加される形で行われる。
print items | command
こうすることによって出力を、ファイルに対して行う代わりにpipeを通して渡 す事ができる。このやり方のリダイレクションは commandへのパイプをオープ ンし、そのパイプを通して、 commandを実行する為に作られたプロセスへ itemsの値を出力する。 リダイレクションの引数commandawkの式でも良い。 その式の値はコマンドを実行するための文字列に変換される。 次の例では二つのファイルが作られる。一つはソートされていないBBSの名前のリスト。 もう一つはアルファベットの降順にソートされたリストである。
awk '{ print $1 > "names.unsorted"
       print $1 | "sort -r > names.sorted" }' BBS-list
この例のソートされていないリストは、ソート済みリストがパイプを通って sortユーティリティが出力する間に、通常のリダイレクトで書き込まれる。 次の例はリダイレクトを使って、メイリングリスト`bug-system'に従ってメイ ルを送るものである。これはトラブルにあったときに、システムメンテナンスの為に 定期的にawkスクリプトを実行するような場合に便利だろう。
report = "mail bug-system"
print "Awk script failed:", $0 | report
print "at record number", FNR, "of", FILENAME  | report
close(report)
ここでclose関数を呼んでいるのは、すぐにパイプを閉じて出力を送る為であ る。 セクション 出力ファイルやパイプのクローズを参照. により詳しく記述さ れている。この例はファイル、コマンドを与えるのに変数を使っている。つまり常に 文字列定数を使う、というわけではないということである。 awk が毎回文字 列値を要求するので、変数を使うというのは一般にはいいアイデアである。

`>', `>>', `|' を使って出力をリダイレクトするときに、filecommandがそれまでにプログラム中で使っていなかったり出力する直前にク ローズを行っていたりすると、ファイルやパイプをオープンする為にシステムに問い 合わせを行う。

出力ファイルやパイプのクローズ

ファイルやパイプがオープンされたとき、そのファイル名やコマンドは awk によって記憶されていて、後で同じファイルや同じコマンドに出力を行ったときには 先程の出力に追加して出力する。ファイルやパイプはawkが実行終了するまで オープンされている。これは大概の場合はうまくいく。

ときとして出力ファイルやパイプを早めにクローズしなければならないときがある。 この為にclose関数を使う。例を挙げよう。

close(filename)

あるいは

close(command)

引数のfilenamecommandは、その値がファイルやパイプをオープン する為に使える文字列であるならば式の形をとっていてもよい。例えば、次のように パイプをオープンしたならば

print $1 | "sort -r > names.sorted"

次のようにクローズを行わなければならない。

close("sort -r > names.sorted")

出力ファイルをクローズする必要がある理由にはいくつかある。

closeはクローズに成功した時0を返し、失敗したときに0以外を返す。 失敗した場合には変数ERRNOにエラーの発生を示す文字列がセットされる。

標準入出力ストリーム

実行中のプログラムは、慣習的に三つの入出力ストリームを何もしなくても読み込み、 書き込みのために使えるようになっている。それらは標準入力, 標準出 力, 標準エラー出力として知られている。これらのストリームはデフォルト でターミナルの入出力に割り当てられている。しかし、しばしばシェルでのリダイレ クトによって(`<', `<<',`>', `>>', `>&' `|'と いったオペレータを使って)、変更される。標準エラー出力はエラーメッセージの出 力にだけ使われる。それは、標準出力、標準エラー出力の二つのストリームがあり、 別々にリダイレクトを行う事が可能であるからである。

他のawk処理系では、次のような方法でしか awkプログラ ムから標準エラー出力にエラーメッセージを出力する事ができない。

print "重大なエラーが発生しました!\n" | "cat 1>&2"

これは標準エラー出力にアクセスするためにパイプを開いてawkプロセスの出 力を渡している。これはエレガントとは言い難いし、他のプロセスを必要とする非効 率的なやり方である。多くの人はこの点にあまり気を使っていないようなawk プログラムを書いている。こういったやり方に代えて、エラーメッセージを次のよう にしてターミナルに送る。

NF != 4 {
   printf("%d行目をスキップしました: フィールド数が4つでありません\n", FNR) > "/dev/tty"
}

これは多くの場合同じ結果となるだろうが、常に、ではない。標準エラー出力は通常 ターミナルであるが、リダイレクトすることができ、その場合には出力はターミナル に対しては行われない。事実、awkをバックグラウンドジョブで実行すると、 そのプロセスはターミナルを持っておらず、`/dev/tty'のオープンは失敗する。

gawkは三つの標準ストリームにアクセスするために特殊なファイル名を持っ ている。gawkで入出力をリダイレクトするときにリダイレクト先のファイル 名が次の特殊な名前の中にあれば、gawkは直接(定義されている)ストリー ムを使用する。

`/dev/stdin'
標準入力(ファイルディスクリプタ0)
`/dev/stdout'
標準出力(ファイルディスクリプタ1)
`/dev/stderr'
標準エラー出力(ファイルディスクリプタ2)
`/dev/fd/N'
ファイルディスクリプタnでアクセスされるファイル。このファイルはawk を実行しているプログラムによって(典型的なのはシェル)、オープンされていなけ ればならない。特別な操作をしない限り、ディスクリプタの0,1,2だけが使用可能で ある。

`/dev/stdin', `/dev/stdout', `/dev/stderr'等のファイル名は それぞれ`/dev/fd/0',
`/dev/fd/1', `/dev/fd/2'の 別名になっている。

gawkプログラムでエラーメッセージを出力する適当な手段は次の様に `/dev/stderr'を利用することである。

NF != 4 {
  printf("line %d skipped: doesn't have 4 fields\n", FNR) > "/dev/stderr"
}

gawkgawkが実行されているプロセスの情報にアクセスするために特 殊なファイル名を与えられている。これらの"ファイル"はそれぞれ、ひとつの情報 のレコードを表わしている。二度以上読み込むためには最初にcloseをつかっ てクローズしなければならない。 (セクション 入力ファイルやパイプのクローズを参照) ファイルの名前は

`/dev/pid'
このファイルを読むと、カレントプロセスのプロセスIDが行末に改行がついた十進数 で返る。
`/dev/ppid'
このファイルを読むと、カレントプロセスの親プロセスのプロセスIDが行末に改行が ついた十進数で返る。
`/dev/pgrpid'
このファイルを読むと、カレントプロセスのプロセスグループIDが行末に改行が ついた十進数で返る。
`/dev/user'
このファイルを読むと改行が終わりにあるレコードがひとつ返る。 フィールドは空白で区切られ、個々のフィールドは次に挙げるような 情報を保持している。
$1
getuid システムコールの値。
$2
geteuid システムコールの値
$3
getgid システムコールの値
$4
getegid システムコールの値
もしなんらかのフィールドが加えられるなら、それはgetgroupsシステムコールが 返すグループIDである(マルチプルグループはすべてのシステムでサポートされて いるわけではないだろう)。

これらの特殊ファイル名はコマンドライン上で、 awkプログラム中でI/Oリダイレクトされるデータファイルのように 使われ、`-f'がついたソースファイルのようには使われないだろう。

これらの特殊なファイル名の認識は互換モードで動作しているときには 無効となる(セクション awkの起動を参照).

警告: あなたの使うシステムが`/dev/fd'というディレクトリ (あるいは先に述べたような特殊ファイル)を持っていないのであれば、これらのファ イル名をgawkそれ自身が解釈する。例えば、 `/dev/fd/4'を使ってファ イルディスクリプタの4 番に出力しようとすると、ファイルディスクリプタの4番が dupされ、その結果の新しいファイルディスクリプタに出力が行なわれる。こ のことはほとんどの場合重要なことではないが、重要なのはファイルディスクリプタ の0、1、2番に関係するファイルをクローズしないということである。もし、これら のうちどれかをクローズしてしまったならば、その結果は予測できないものとなる。


移動先 先頭, , , 末尾 セクション, 目次.