[冒頭] [目次] [見出し] [ ? ]

General Introduction

このファイルは、ファイルから特定のレコードを選び出すことに使ったりそれ らを操作することができるプログラム awk について記載したもの である。

Copyright © 1989, 1991, 1992, 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.

これは AWK の GNU による実装の 3.1 .6 (ま たはそれ以降) のバージョン用の GAWK: Effective AWK Programming : GNU Awkのためのユーザーズガイド の3 版である。

複製は許可されており、Free Software Foundation によって発行されている GNU Free Documentation License の 1.2 以降のバージョンの "GNU General Public License" に含まれる Invariant Sections、すなわち (a) に含まれ る表紙のテキスト (以下を参照)、そして (b) に含まれる裏表紙のテキスト ( 以下を参照) と一緒の元でこのドキュメントを配布そして/または改版すること も認められている。ライセンスの複製は "GNU Free Documentation License' ' というタイトルの章に含まれている。

  1. "GNU マニュアル"
  2. "この GNU マニュアルの複製と改変は GNU ソフトウェアと同じように自由で ある。Free Software Foundation で発行されたコピーは GNU 開発の基金募集 とする。"

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

序文

Arnold Robbins と私は良き友人である。ある事情 -- そして我々の好きな言語、 AWK により 11 年前に知り合った。その事情というのは 2 年早くはじまった。私は 新しい仕事に就いて隅にあった接続されていない Unix コンピュータに気が付いた。 誰も使い方を知らず、私も知らなかった。しかし、1 年後には稼動し、そして私は root になり一人だけのユーザーになった。ある日、私は統計学者から Unix プログラマーになりはじめていた。

Unix の本を探しに図書館や書店に何度も足を運んだある時、灰色の AWK 本、つま り 1988 年に Addison-Wesley から出版された Aho, Kernighan と Weinberger の 書いた The AWK Programming Language を見付けた。AWK の簡単なプログラ ミングパラダイム -- 入力ファイルからパターンを見付けてアクションを処理する -- はしばしば複雑なことを簡単にしたり退屈なデータの生成を数行のコードで 簡単にすることができた。私は AWK でプログラムするために手を動かすことに興 奮していた。

悲しいことに、私のコンピュータの awk は AWK の本の中に記述された言 語の限定されたものであった。私は自分のコンピュータのものは " 古い awk" であることに気づき AWK の本は " 新しい awk" につ いて記述されていることに気づいた。私はこれが独特のものであることを学んだ; 古いバージョンは脇に追いやられたりその名前を放棄された。システムに新しい awk が入っていれば、いつも nawk と呼ばれ、いくつかのシ ステムには入っていた。新しい awk を入手する最良の方法は prep. ai.mit.edu から gawk のソースコードを ftp することであ る。gawk は David Trueman と Arnold によって書かれた新しい awk のバージョンであり、GNU General Public License のもとで使用可 能である。

(偶然にも、新しい awk を見つけ出すのに時間はかからなかった。 gawk は Linux と一緒に出回っており、ほとんど全てのシステムに 対してバイナリとソースコードをダウンロードできる; 妻は彼女の VMS box で gawk を使っている。)

私の Unix システムは壁から接続されない状態から始まった; 確かにネットワ ークには接続されていなかった。だから、普通の gawk や Unix コ ミュニティのの存在は知る由もなく、新しい awk を切望し、自分 自身で mawk と呼ばれるものを作成した。gawk のこと を知り終える前であり、止めるには遅すぎ、結局 comp.sources ニュ ースグループに投稿した。

投稿から数日経って、Arnold から紹介をした親切な email が届いた。彼は設 計とアルゴリズムを共有することを提案し AWK の本の出版後に加わった言語 拡張のサポートを mawk にアップデートできるように POSIX 準拠 のドラフトを添付していた。

正直に言えば、もし我々の役割が反対であったら、私はそんなにオープンにな ることもなかったし、多分めぐり合うこともなかった。私はめぐり合うことが できたことを嬉しく思っている。彼は AWK のエキスパート中の AWK エキスパ ートであり本当に良い人である。Arnold は専門技術に対し凄い量の貢献をし Free Software Foundation に時間も貢献している。

この本は gawk のリファレンスマニュアルであるが、本当は広く対 象者にアピールしている AWK のプログラミングに関する本である。1987 年に Bell 研によってリリースされたもので定義され 1992 年に POSIX ユーティリ ティ準拠に分類された AWK 言語の決定版リファレンスである。

一方、初心者 AWK プログラマーは AWK の基礎的なイディオムを強化させる大 量の練習プログラムを学ぶことができる: データドリブンな制御フロー、正規 表現でのパターンマッチングそして連想配列などである。何か新しいことを追 い求めるのであれば特別な `/inet' ファイルを通してネットワークプロ トコルへの gawk のインターフェイスを実際に使ってみればよい。

この本のプログラムは AWK のプログラムが開発するのに C で書かれた同等の ものよりも典型的な場合は非常に小さくそしてより速いことを明確にするもの である。それゆえ、しばしばアルゴリズムのプロトタイプや素早く動作させる ために AWK でデザインし問題を早期に発見することは利点がある。しばしば、 インタープリタのパフォーマンスは十分に満足するものであり AWK のプロト タイプが製品になっていくことがある。

新しい pgawk (gawk のプロファイリング) はプログラ ムの実行カウントを与えてくれる。最近 n 行の入力に対してのアルゴ リズムで経験した。 ~ C n^2 のパフォーマンスを示したが、これは理論的には ~ C n log n の振る舞いをすると予想できる。しばらく考え込んだが、`awkprof.out' プロファイルがコードの 1 行を的確に示してくれた。ぜひ pgawk をプログラマの道具箱に迎え入れたい。

Arnold は長年に及ぶ経験からこの本の中から AWK プログラムの記法と用法、 そして gawk の開発を引き出している。もし、AWK を使ったり学び たい時には、この本を読むべきだ。

 
Michael Brennan
mawk の作者

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

前書き

幾つかの種類のタスクはテキストファイルを処理するときに繰り返し現れる。 あなたは特定の行を取り出して残りを捨てたいと考えるかもしれない。 あるいは特定のパターンが現れたところでは変更を行うようにしたいが 残りはそのままにしておきたいと考えるかもしれない。 これらのタスク行うためにを C や C++, Pascal のような言語で専用のプログラムを 記述することは時間を浪費するし非効率である。 このような仕事は awk では簡単にできる。 awk ユーティリティは単純なデータの再フォーマットのような 仕事を簡単に扱うことのできる特殊用途プログラミング言語を解釈する。

GNU の実装による awkgawk と呼ばれ、 System V Release 4 version の awk に対して完全上位コンパチブルである。 gawk はまた、POSIX の定めた awk 言語の規定に対しても上位互換である。 これは、きちんと書かれた awk のプログラムは gawk でも動作する。 ということである。したがって、このマニュアルでは gawk と他の awk との実装上の違いを述べるようなことはしない。

awkを使ってできることは:

それに加えて、gawkは以下のようなことも簡単にできる 機能を提供している:

この Web ページ では awk 言語そのものについてと、 その効果的な使い方を教えるものである。 catls(1) のような基本的なシステムコマンドや、 入出力リ ダイレクトやパイプのような基本的なシェルの機能ついて慣れ親しんでいたほうがよい。

awk 言語の処理系は多くの異なる環境において使用可能である。 この Web ページ では一般に awk 言語全般についての説明を行い、 同様に gawk (which stands for "GNU Awk") と呼ばれるある特定のawkの実装についての説明を行う。 gawk は多くのUNIXシステム、 - 下は 80386 搭載のPCベースのコンピュータから、 上は Cray のような大きなシステムにいたるまで - で稼動している。 gawk はまた Mac OS X や、MS-DOS や OS/2 が動作する PC、Atari、 Amigaなどのマイクロコンピュータ、BeOS、Tandem D20、そしてVMSにも移植されている。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

awkgawk の歴史

プログラミング言語のためのレシピ

1 部分の egrep

1 部分の snobol

2 部分の ed

3 部分の C

lexyacc を使うために全ての部分をよくかき混ぜる。 最小限のドキュメントとリリース。

8 年後、egrep の他の部分を加えて C からもう 2 つの部分を加える。 ドキュメントを整備してリリース。

awk の名前はその開発者達の名前、Alfred V. Aho, Peter J. Weinberger そして Brian W. Kernighan から来ているものである。 awk のオリジナルバージョンは 1977 年に AT&T のベル研究所で開発された。 1985 年にはユーザー定義関数、複数の入力ストリーム、 動的正規表現の導入によってより強力なプログラミング言語となったバージョンが開発された。 この新しいバージョンは一般的には UNIX System V Release 3.1 で使えるようになった。 System V Release 4 での新 awk では幾つかの新しい機能追加と、 言語の"暗い隅"をきれいにする作業が行われた。 POSIX のコマンド言語およびユーティリティの標準は gawk のデザイナーとオリジナルを開発したベル研究所の awk 開発者の両方からのフィードバックが反映されている。

Paul Rubin は 1986 年に GNU による実装である gawk を作成した。 Jay Fenlason がそれを Richard Stallman のアドバイスを受けつつ完成した。 John Woods はコードの一部分に寄与した。 1988 年と1989 年に、David Trueman は私 (Arnold Robbins) の助けの元に gawk に新しい awk との互換性を持たせるための作業をおこなった。 1995 年頃、わたしは主たるメンテナーとなった。 現在の開発の主たる点はバグの修正、パフォーマンスの向上、標準に対する準拠であり、 そして随時新しい機能の追加を行っている。

1997 年 5 月に、Jürgen Kahrs は awk にネットワークアクセスの機能の 必要性を感じ、わたしのちょっとした助けを伴って gawk に機能を追加した。このとき、彼は TCP/IP Internetworking with gawk というドキュメントも書いた (別のドキュメント。これは gawk の 配布に含まれている)。彼のコードは最終的に gawk の一部分となり gawk の バージョン 3.1 で統合された。

See section gawk に対する主要な貢献者たち, gawk へ重要な貢献をしてくれた方の完全なリスト。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

0.1 バラは他のどんな名前でも同じように香る

awk 言語は何年にも渡って発展してきた。 その詳しい説明は awk 言語の進化 で述べられている。 この Web ページ で説明されている言語はほとんどが "new awk" (nawk) として知られているものである。

このため、多くのシステムでは複数のバージョンの awk が存在する。 一部のシステムはオリジナルバージョンの awk 言語のインプリメントである awk ユーティリティと、新しいバージョンである nawk ユーティリティを持っている。 他の一部は "古いawk" である oawk を持っている。 残りのものは一つのみ、通常は新しいほうを awk として持っている。 (2)

重要な事は、このことによってあなたが書いたプログラムをどのバージョンの awk で実行すればよいかということを知ることが難しくなるということである。 ここで私達ができる最善のアドバイスは、あなたのシステムのドキュメントを確かめて欲しい。 ということである。gawk に対してと同じくらい awk, oawk, nawk を見て欲しい。 あなたが使っているシステムには新しいバージョンの awk があり、 あなたのプログラムを実行するときにはそれを使ったほうがよい局面があるかもしれない (もちろん、あなたが この Web ページ を読めば、gawk を持つ機会があるわけだ!)。

この Web ページ を通じて、POSIX での awk の実装が備えている言語の機能を 示すときには、単純に awk という語を使う。 GNU での実装に特有な機能を示すときには gawk という語を使用する。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

0.2 この本を使う

awk という語は特定のプログラムを示していると同時に あなたが実行しようとしているプログラムの言語を示している。 気をつける必要がある場合には、プログラムを"awk ユーティリティ" と呼び、 もう一方を "awk 言語" と呼ぶ。 gawk という語は GNU プロジェクトが開発した awk を表している。 この Web ページ の目的は awk 言語と、 awk ユーティリティの実行の仕方の両方を説明することである。

この Web ページ の主な目的は POSIX の標準で定められている awk の機能を説明することである。 それはある特定の実装や gawk を説明しているときに行っている。 その説明では同様に、gawk と他の awk の実装との重要な差違を 説明することを試みる。(3) さらに、POSIX 標準の awk にはないような gawk の機能を説明する。

この Web ページ はチュートリアルとリファレンスの両方の役目を果たすという 難しい立場のものであるが、もし、あなたが初心者ならば難しすぎると感じるような 記述の部分は好きに読み飛ばしてかまわない。そういった部分はエキスパートユーザーと、 このマニュアルのオンライン Info バージョンで参照されるためのものだからだ。

Web ページ を通して散りばめられた Advanced Notes という ラベルの小節がある。 関係する点の完全な説明が加えられているが、最初に読むのに興味をそそるも のではない。 見出しに現れ、"高度な機能" と最初に付けられている。

ほとんどの場合、例は完全な awk プログラムを使っている。 より上級の章のいくつかは、現在説明しているコンセプトを示す awk プログラムの一部のみが現れる。

この Web ページ は主に awk にさらされていない人々を対 象としているが、awk の上級者にも有用な情報を多く含んでいる。 特に、POSIX awkawk 関数のライブラリ のプログラム例や Practical awk Programs のものは興味深い。

Getting Started with awk には awk を使い始めるにあたって 知っておく必要があることの概要がある。

正規表現 では一般的な正規表現を紹介し、それに加えて POSIX awkgawk に固有のものを紹介している。

入力ファイルを読む では awk がどのようにデータを読むのかを 説明している。 レコードとフィールドのコンセプトを導入し、getline コマンドについても説明している。 入出力のリダイレクションは最初にここで説明している。

Printing Output では awk プログラムが どのように printprintf を使って出力を 行うことができるのか説明している。

ではプログラムにおいて基本的なものである 式について説明している。

パターン、アクション、変数 では レコードのマッチングのためのパターンや レコードがマッチしたときのアクションをどのように記述するかを 説明し、awkgawk が使う組み込み変数についても 述べている。

awk における配列 では awk のただ一つのデータ構造である 連想配列についてカバーしている。 配列の要素や配列全体を削除することや gawk で配列をソートすることについても述べている。

関数 では awkgawk で提供されている 組み込み関数について説明していて、さらに自分用の 関数の定義の仕方についても述べている。

gawkの国際化 では、 の異なる言語への実行時のプログラムメッセージの gawk の翻訳機能について述べている。

gawk の高度な機能 では、 gawk に固有の幾つかの先端的機能である 他のプロセスとの間の双方向通信、TCP/IPネットワーキング、 awk プログラムのプロファイリングについて説明している。

awkgawk の実行 では、gawk の実行の仕方、 コマンドラインオプションの意味、awk プログラム ソースファイルの見つけ方について説明している。

awk 関数のライブラリPractical awk Programs では、 数多くのサンプル awk プログラムを集めている。 ここを読むことであなたが awk を使って実際の問題を 解決できるようになるだろう。

awk 言語の進化 では、awk 言語が最初のリリースから どのように発展してきたのかを説明する。また、gawk が 機能を拡張してきた経緯についても述べる。

gawk のインストール では、gawk の入手の仕方、 Unix でのコンパイルの仕方、非 Unix システムでのコンパイルの仕方と 使い方について説明する。また、gawk のバグの報告の 仕方や他の三つの自由に入手することのできる awk 処理系に ついても述べている。

実装に関するノート では、 gawk 固有の拡張の抑制の仕方、 gawk への貢献の方法、 拡張ライブラリの記述の仕方、 いくつかの gawk の開発における将来の方向性 について述べている。

基本的なプログラミングの概念 では、 コンピュータプログラミングにあまり親しんでいない人たち向けに 非常に大まかにバックグラウンドについて説明している。 また、浮動小数点数に関することがらについて幾つかの議論を まとめている。

用語集 には、 すべてではないけれどもこの本を通して使われている特長的な単語の 定義を集めている。 もし見慣れない単語を見つけたら、ここで確認してみて欲しい。

GNU General Public LicenseGNU Free Documentation License には gawk のソースコードと、 この DOCUMENT をカバーしているライセンスがある。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

0.3 Typographical Conventions

この Web ページ は Texinfo、つまり GNU ドキュメンテーションフォ ーマット言語を用いて書かれている。 ひとつの Texinfo のソースファイルはドキュメントを表示とオンラインバー ジョンの両方を提供するのに用いられている。 そのため、典型的な変換によって目にする他の書籍とは少し異なっている。

コマンドラインで打ち込む場合の例として一般的な shell を最初に次にプロ ンプト、そして `$'`>' とする。 コマンドからの出力はグリフ "-|" を先頭に書く。 これは典型的にコマンドの標準出力を表現している。 エラーメッセージ、そして他の出力の標準エラーはグリフ "error-->" を先 頭に書く。 例を挙げる:

 
$ echo hi on stdout
-| hi on stdout
$ echo hello on stderr 1>&2
error--> hello on stderr

テキスト中には、コマンド名が このフォント で表され、コードのセ グメントは同じフォントでクォートされて `このように' 表される。 いくつかは このように 強調され、もし強調したい場合には このように なる。 最初に新しい述語が出現した際には普通 定義 とし、この文書で前に ` `定義" が出現したものと同じフォントである。 ファイル名 はこのように表現される: `/path/to/ourfile'

キーボードからでタイプする文字は このように 見える。 特に、"コントロール文字"と呼ばれる特別な文字がある。 CONTROL キーと他のキーの両方を同時に押すような文字がある。 例えば、Ctrl -d は最初に CONTROL キーを押し続けて、 次に d を押して最後に両方のキーを離す。

Dark Corners

ダークコーナーは基本的にフラクタルである -- どんなに注意深くしても、 常に小さく暗いところがある。
Brian Kernighan

POSIX 標準までは (そして The Gawk Manual までは)、多くの awk の特徴が貧弱に表記されていたか、全て記載されていなかった。 そのような特徴の表記 (しばしば "ダークコーナー" と呼ばれている) はこ の Web ページ に注記してある "(d.c.)"。 これらは "ダークコーナー" と名打って見出しの中に出現している。

最初に注記するが、ダークコーナーの範囲は、定義上、不完全なものである。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

GNU プロジェクトとこの本

フリーソフトウェア財団 (Free Software Foundation, FSF) は 非営利団体で、自由に配布することのできるソフトウェアの作成と配布を 目的としている。Emacs エディターのオリジナルの作者である Richard M. Stallman によって設立された。 GNU Emacs は今日最も広く使われている Emacsである。

GNU(4) プロジェクトはフリーソフトウェア財団によって作業が行われている 完全な、自由に配布することのできる POSIX コンパチブルな コンピューティング環境の作成を目指したものである FSF は "GNU General Public License- GNU一般公有許諾 -" (または GPL) を使って、 そのソフトウェアのソースコードが常にエンドユーザーに とって入手可能な状態にあることを保証している。 GPL のコピーはこの No value for DCOUMENT にも含まれている (see section GNU General Public License)。 GPL は C で書かれている gawk のソースコードに適用されている。 FSF や GNU プロジェクトについての情報をオンラインでもっと見つけるには the GNU Project's home page を見て欲しい。 このドキュメント(の原文)は their web site にもある。

シェル、エディター(Emacs)、高度な移植性を備えたC、C++、Objective-C コンパイラー、 シンボリックデバッガー、その他 (gawk のような) 大小たくさんのユーティリティが すでに完成しており、自由に入手できる。 GNU オペレーティングシステムのカーネル (HURD) は既にリリースされている。 しかし、これはまだ開発の初期段階にとどまっている。

GNU オペレーティングシステムの開発がさらに進むまでは、 自由に配布することができ、80386、DEC alpha、 Sun SPARC、IBM S/390 などで動作する UNIX に似たオペレーティングシステムである GNU/Linux を使うのがよいだろう (5)。 GNU/Linux に関しては多くの書籍があり、 その中の一つに Matt Welsh による自由に入手できる (freely availabke)な Linux Installation and Getting Started がある。 コンピュータストアや CD-ROM が添付された Linux に関する書籍を通じ、 多くの GNU/Linux 配布が入手可能になっている。 (二つの自由に入手可能な Unix ライクな 80386 等向けのオペレーティングシステム、 NetBSD と FreeBSD がある。これらは両方とも 4.4-Lite Berkeley Software Distribution に基づいたものであり、それら用の awk として gawk の最新バージョンが使用されている)。

今あなたが読んでいるこの Web ページ はフリーである。 ここにある情報や、この Web ページ の元となった gawk に付随する機械読み取り可能なソースコードは 誰もが自由に入手できるものであり、 そして誰もがこのマニュアルを好きなだけコピーすることができる (ちょっと時間を取って GNU Free Documentation License で Free Documentation Licenseを チェックしてみて欲しい。

自分で印刷するよりも、製本された本の方が読みやすいし使いやすい。 それに加えて、この本の売上による利益はFSFが より一層の自由なソフトウェア (free software) を開発する助けとなる。

このWeb ページ それ自身は以前からあったいくつかの版を通しての結果である。 Paul Rubinは The GAWK Manualの非常に初期のドラフトを書いたが それは40ページほどのものだった。 Diane Close と Richard Stallmanはそれを改良し、90ページほどのもの になったがオリジナルの"古い"バージョンのawkに関する説明が ほとんどなかった。

わたしは1988年の秋にそのバージョンを本に作業を始めた。 FSFはいくつかのpreliminary バージョン (0.xという数字のついたもの) を出版した。 1996年に、gawk 3.0.0と共に 第1版がリリースされた。 FSFはその最初の二つの版をThe GNU Awk User's Guideという タイトルで出版した。

gawk で配列の値や添え字をソートする, as well as gawk のビット操作関数, gawkの国際化, and also gawk の高度な機能, and 新たな組み込み関数を gawk に追加する. この版では基本的な部分は第一版のものから構成されているが、 gawkのバージョン 3.1 での 新しい機能の説明などが追加されている。 gawk で配列の値や添え字をソートするgawk のビット操作関数gawkの国際化gawk の高度な機能新たな組み込み関数を gawk に追加するなどがそうである。

GAWK: Effective AWK Programming は進化しつづけている。 電子バージョンはFSFからgawkと共に配布されている。 もしこのWeb ページ に間違いを見つけたら、どうかそれを知らせて欲しい! 問題の報告の方法はSee section 問題点やバグを報告するにある。 あるいは、出版社気付でわたしに手紙を書いて欲しい。
訳注: 日本語版に関しての間違いは訳者まで。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

貢献の仕方

GNU awkのメンテナーとして、わたしは 公的に入手可能なawkプログラムを集め始めた。 詳細は ftp://ftp.freefriends.org/arnold/Awkstuffを参照のこと。 もしあなたが面白いawkプログラムや gawk拡張を書いて、それを世界の人々と分かち合いたいと 思ったなら、どうぞわたし(arnold@skeeve.com)に連絡して欲しい。 インターネットでできることはgawkの配布を 制御可能なサイズにする。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

謝辞

The GAWK Manualの初期のドラフトでは、以下のような謝辞があった。

このマニュアルの作成にあたって、多くの人々の協力があった。 Jay Fenlasonは多くのアイデアと、サンプルプログラムを寄付してくれた。 Richard Mlynarik と Robert Chassellはこのマニュアルの草稿について有用な助言をくれた。 UC San DiegoのChemistry DepartmentのJohn W. Pierceによる A Supplemental Document for awkという論文は awkの実装とこのマニュアルの両方に関連した幾つかの問題点を指摘しており、 我々にとって有益であった。

ここでRichard M. Stallmanと、彼のより良き世界へのビジョン、 そしてフリーソフトウェア財団を設立し GNUプロジェクトを開始した彼の勇気を認めたい。

以下の方々 (アルファベット順) によって、今までとこの版も含めて、この書籍の様々な版に有益なコメントが提供された。 Rick Adams, Nelson H.F. Beebe, Karl Berry, Dr. Michael Brennan, Rich Burridge, Claire Cloutier, Diane Close, Scott Deifik, Christopher ("Topher") Eliot, Jeffrey Friedl, Dr. Darrel Hankerson, Michal Jaegermann, Dr. Richard J. LeBlanc, Michael Lijewski, Pat Rankin, Miriam Robbins, Mary Sheehan, and Chuck Toporek.

Karl Berry helped significantly with the TeX part of Texinfo. Robert J. ChassellはTexinfoの使用についての助言をしてくれた。 彼はこのWeb ページ のタイトルを How To Gawk Politely というタイトルにしないよう助言をしてくれた。 Karl Berryは TexinfoのTeX部分に際立った助力をしてくれた。

この Web ページ と gawk 自体を素晴らしく進歩させれくれ、自宅での静かな休暇の多くを捧げてくれた シアトルの Marshall と Elaine Hartholz とデトロイトの Bert 博士と Rita Schreiber に感謝する。

SSC の Phil Hughe は彼のラップトップ GNU/Linux システムを貸し出してくれるという非常に重要なことを提供してくれ、2 回目は家から離れたところから多くの仕事をこなすことができた。

David Truemanは特別に賞賛を受けるに値する。彼はgawkを性能がよく、 バグがないものとする不屈な仕事を行った。 彼はもうgawkに関係していないが、 このプロジェクトで彼とともに作業したことは非常に喜ばしいことであった。

GNITS メーリングリストの大胆不敵なメンバーと、もっとも崇高な Ulrich Drepper は、言い表せないほど助けてくれて国際化の機能の設計をフィードバックしてくれた。

Nelson Beebe, Martin Brown, Andreas Buening, Scott Deifik, Darrel Hankerson, Isamu Hasegawa, Michal Jaegermann, Jürgen Kahrs, Pat Rankin, Kai Uwe Rommel, and Eli Zaretskii (順不同) は長期間にわたりgawkの "crack portability team"のメンバーである。 彼らの作業と助けを抜きにしてgawkは今日あるような 優秀なプログラムにはならなかっただろう。 このチームの素晴らしい面々と作業を行うことは喜ばしいことであった。

gawkのテストとデバッグの間貴重な助言を寄せてくれ、 言語に関する幾つかの疑問点を明確にするための助けをしてくれた Bell研究所の Brian Kernighan に感謝する。 彼の助けなくして我々はgawkやこのドキュメントの作業において 良い結果は得られなかっただろう。

O'Reilly & Associates の Chuck Toporek, Mary Sheehan と Claire Coutier は gawk 3.1 のこの Web ページ の編集を手伝っていただいた。

私の素晴らしい妻、Miriam に、このプロジェクトの多くのバージョンを通して耐えてくれたこと、校正、そしてコンピュータと一緒にいさせてくれたことに感謝する。 両親の愛と、私を育てて教育してくれたことに感謝したい。 最後ではあるが、神にこのようなことをする多くの機会を与えてくれたことに感謝し、こうした機会を好機にしてくれたことにも感謝しなければならない。



Arnold Robbins
Nof Ayalon
ISRAEL
March, 2001


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1. Getting Started with awk

awkの基本的な機能は、ファイルからあるパターンを含んでいる行 (もしくは他のテキストの構成単位)を検索することである。 ある行がパターンの一つ にマッチしたとき、 awkは特定のアクションをその行に対して実行する。 awkはこのようにして入力ファイルの最後の行までそれぞれの行を処理し続ける。

awkのプログラムは他の大部分の言語とは異なっていて、 データ駆動(data-driven)である。 これは処理したいと思うデータについて記述し、さらにそれを見つけたときに どのようなことをするのかということについて記述する。ということである。 他のほとんどの言語は手続き型(procedural)である。それらにおいては、 事細かに、プログラムの各ステップ毎にどのようにするかを記述しなければならない。 手続き型言語を使用しているときは、あなたがプログラムで処理しようとしている データがどのような構造をしているかを明確に記述することは、一般的には簡単ではない。 このため、awkプログラムは、 しばしば書くのも読むのもやさしいということになるのである。

awk を実行したときにawk がどのように動作するかを awk プログラムで指定できる。 プログラムはrulesの集まりからなる(関数定義も含まれるが、 高度な機能なので今のところは無視する。 See section ユーザー定義関数.)。 個々のルールはある特定のパターンを探し、 見つかったパターンで定義されたアクションを実行する。

文法的には、ルールはパターンとそれに続くアクションから構成される。 アクションはカーリーブレースによってアクションと区切られる。 ルールは一般的には改行によって区切られる。 その結果、 awk プログラムは以下のような形式となる。

 
パターン { アクション }
パターン { アクション }
…

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.1 awk プログラムの実行の仕方

awkプログラムを実行するには幾つかの方法がある。 プログラムが短いのならば、下の例のようにawkを実行するコマンドに 含めてしまうのがもっとも簡単である:

 
awk 'プログラム' 入力ファイル1 入力ファイル2

プログラムが長い場合、プログラムをファイルにしてしまい、 次のようにしてコマンドと一緒に実行するのが一般的には便利である:

 
awk -f プログラムファイル 入力ファイル1 入力ファイル2

このセクション では両方のメカニズムについてバリエーションも含めて 説明する。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.1.1 使い捨ての一発awkプログラム

一度awkに慣れ親しんでしまえば、単純なプログラムを必要とするときに タイプするようになるだろう。そのようなとき、 そんなプログラムはawkコマンドの最初の引数として記述できる。このように:

 
awk 'プログラム' 入力ファイル1 入力ファイル2

プログラムは先に説明したように、 パターンアクション の並びからなる。

このコマンドはシェルやコマンドインタープリターに対してawk を起動させ、プログラムが入力ファイル中のレコードを処理するように 指示するものである。プログラムの周りには引用符があり、これによってシェルは、 シェルにとってのスペシャルキャラクタとなるawkのキャラクタを 誤って解釈することがなくなる。同時にこれは、 シェルに対してプログラムawkに対する一つの引数にし、 またプログラムが複数行に渡ることも可能にする。

この書式はawkプログラムを別のファイルに分ける必要がないので、 短いものから中程度の大きさのawkプログラムを シェルスクリプトから実行するのに便利である。 自己完結した(self-contained)シェルスクリプトは分割されているファイルを 間違った場所に置くことがないので、より信頼性がある。

この章 の後の方にある 幾つかの単純な例に幾つかの短い自己完結したプログラムがある。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.1.2 awk を入力ファイルなしで実行する

awkを入力ファイルなしで実行することもできる。 それにはこうコマンドラインでタイプすればよい。

 
awk 'プログラム'

このとき、awkプログラムの入力を標準入力にする。 標準入力は通常、あなたが端末からタイプしたものである。 これはあなたがCtrl -dをタイプしてファイルの終端を指示するまで続く (他のオペレーティングシステムではファイルの終端を示すキャラクタは違ったものである。 例えばOS/2やMS-DOSでは Ctrl -zである)。

(BEGIN is a feature we haven't discussed yet): 以下の例にあるプログラムは親切なアドバイスを出力し (Douglas Adamsの The Hitchhiker's Guide to the Galaxyより)、 コンピュータプログラミングの複雑さに対して恐れないようにする (BEGINはまだ説明していない機能である):

 
$ awk "BEGIN { print \"Don't Panic!\" }"
-| Don't Panic!

このプログラムは何の入力も読まない。ダブルクォートの前にある `\'は、 シェルのクォートルールのために、特にダブルクォートとシングルクォートを 混ぜて使う場合に必要である。(6)

次なる単純なawkプログラムはcatユーティリティを 模倣するものである。これはキーボードからタイプしたものすべてを 標準出力にコピーする(こんなに短いのにちゃんと働くのだ)。

 
$ awk '{ print }'
Now is the time for all good men
-| Now is the time for all good men
to come to the aid of their country.
-| to come to the aid of their country.
Four score and seven years ago, ...
-| Four score and seven years ago, ...
What, me worry?
-| What, me worry?
Ctrl
-d

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.1.3 長いプログラムを実行する

ときとしてawkプログラムが非常に大きくなることがある。 この場合、プログラムを別のファイルに分けて置くのが便利である。 awkに対してプログラムの入ったファイルを指定するには次のようにタイプする:

 
awk -f ソースファイル 入力ファイル1 入力ファイル2

`-f'awkユーティリティに対してawkプログラムを ソースファイルというファイルから受け取るということを指定する。 任意の名前をソースファイルに使うことができる。たとえば、次のようなプログラムを:

 
BEGIN { print "Don't Panic!" }

`advice'という名前のファイルに置くことができ、次のようなコマンドで使う。

 
awk -f advice

これは次の例とおなじことである。

 
awk "BEGIN { print \"Don't Panic!\" }"

こちらは既に説明した (see section awk を入力ファイルなしで実行する)。 大部分のファイル名はシェルのスペシャルキャラクタを含んでいないので、 通常は`-f'で指定するファイル名を引用符で括る必要はない。 `advice' 中では、awkプログラムは引用符で括られていない。 引用符をつける必要があるのはawkのコマンドライン上で プログラムを与える場合だけである。

もしawkプログラムのファイルの確認を明確にしたいのなら、 ファイル名に`.awk'という拡張子を追加することができる。 これはawkプログラムの実行には影響を及ぼさないが、 "housekeeping"を容易にする。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.1.4 実行可能な awk プログラム

For example, you could update the file `advice' to look like this: 一度awkを学んでしまえば、シェルの`#!' 構文を使って、 独立した awk スクリプトを書いてみたくなるだろう。 多くのUnixシステム(7) でこれが可能である(そしていつかはGNUシステムでも)。

 
#! /bin/awk -f

BEGIN { print "Don't Panic!" }

このファイルを(chmodユーティリティを使って)実行可能にした後で、 シェルから単純に`advice'とタイプするだけで、`awk -f advice' とタイプしたのと同じようにシステムがawkを実行するようアレンジする (8):

 
$ chmod +x advice
$ advice
-| Don't Panic!

(ここで、あなたの使っているシェルの検索パス変数(典型的には$PATH)に カレントディレクトリが含まれていることを仮定している。もしそうでなければ シェル上で`./advice'とタイプする必要があるだろう)

自己完結したawkスクリプトは、それを使う人がそのプログラムを awkで書かれたものであることを知ることなしに起動できるプログラムを 書きたいと思うときに便利である。

特記: `#!' を用いた可般性

一部のシステムでは、インタープリターの名前の長さは32キャラクタまでに 制限されている。しばしば、これはシンボリックリンクを使って 回避することが可能である。

`#!'の行でawkのパスの後に二つ以上の引数を置くべきではない。 これをしてしまうと正しく動作しないだろう。 オペレーティングシステムは行の残りの部分を一つの引数として取り扱い、 それをawkに渡す。これによって混乱した動作がもたらされる。 ありがちなのは、 awkのusageメッセージの類が出力されることだろう。

最後に、ARGV[0](see section 組み込み変数)はあなたの使っている オペレーティングシステムによって 様々なものになることを述べておく。あるシステムでは`awk'が そこに入り、別のあるシステムではawkのフルパス(`/bin/awk' のように)が入り、また別のシステムではスクリプトの名前(ここでは`advice') が入る。スクリプトの名前を得るためにARGV[0]の値を当てにしては いけない。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.1.5 awk プログラム中のコメント

コメントとはプログラム中で、人が(プログラムを)読みやすくするために 含められるテキストであり、プログラムにとって必要不可欠というものではない。 コメントはプログラムが書けるところならどこでも置けるが、何の働きもしない。 身近な全てのプログラミング言語はその様な目的のためにコメントを使える様になっているが、 それは典型的なプログラムというものは何らかの助けなしには 理解するのが非常に難しいからである

awk言語のコメントは`#'で始まり、その行の終わりまで続く。 awk言語は`#'に続く部分を無視する。 例えば、`advice'に次のように追加を行うことができる。

 
# このプログラムは良いフレンドリーなメッセージを表示する。
# 初心者がコンピュータを毛嫌いさせないようにする。
BEGIN    { print "Don't Panic!" }

awkプログラムをキーボードから直接入力しているような場合でも コメント行を入力することは可能であるけれども、これはあまり有効なことではないだろう。 なぜならコメントを使う目的が、後日あなたか、あるいは他の人がそのプログラムを 読んだときに理解するのを助けるためだからだ。

警告: 使い捨ての一発awkプログラムで言及したように、 小規模のプログラムをシングルクォートでくくることが可能であり、 それによってあなたのシェルスクリプトをself-containdなものにできる。 これを行ったとき、アポストロフィ(シングルクォート)を コメント中(もしくはプログラムのどこかで)で使ってはいけない。 シェルはそのようなクォートをプログラム全体をくくるクォートとして扱ってしまう。 結果として、通常はシェルがクォートのバランスが取れていないといった メッセージを出力する。そして、awk が実際に実行されたならば、 構文エラーのような妙なメッセージを出力するだろう。例を挙げる:

 
$ awk '{ print "hello" } # let's be cute'
>

シェルは最初の二つのクォートのマッチを見て、コマンドラインの終わりにある のが新たなクォートオブジェクトの始まりであるとみなす。 したがって二次プロンプトが表示されて入力待ちとなる。 Unixのawkでは、クォートを閉じると以下の結果となる:

 
$ awk '{ print "hello" } # let's be cute'
> '
error--> awk: can't open file be
error-->  source line number 1

`let's'の中のシングルクォートの前にバックスラッシュを置くことは 助けにはならない。なぜなら、バックスラッシュはシングルクォートの中では 特別なものではないからである。 次のサブセクション ではシェルのクォーティングルールについて説明する。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.1.6 シェルのクォーティングについて

訳注:Windows(コマンドプロンプト)には関係しない部分なので後回し。

適度な短さの awk プログラムにとって、awk のコマンドライン上でプログラムを入力できることは最も便利である。 シングルクォートでプログラム全体を囲むのは最良の方法である。 これはシェルプロンプトで対話的にプログラムを入力する場合も、大きなシェルスクリプトの一部を記述する場合も正しい。

 
awk 'program text' input-file1 input-file2

シェル上で作業している際に、シェルのクォートルールの基礎知識は役立つ。 以下のルールは POSIX 準拠の、Bourne スタイルのシェル (bash, GNU Bourne-Again Shell) 上で適用できる。 もし、csh を使っているなら、自己責任で行う。

シングルとダブルクォートを混在させるのはは難しい。シェルクォーティング トリックを使うべきである、具体的には以下のようにする:

 
$ awk 'BEGIN { print "Here is a single quote <'"'"'>" }'
-| Here is a single quote <'>

このプログラムは 3 つの繋がったクォートの文字列で構成されている。 最初と 3 つ目はシングルクォートで、2 番目はダブルクォートである。

これは以下のように"簡略化"できる:

 
$ awk 'BEGIN { print "Here is a single quote <'\''>" }'
-| Here is a single quote <'>

これら 2 つが読みやすいことを理解せよ。

他の選択肢はダブルクォートが用いられ、埋め込まれたものをエスケープ し、awk レベルのダブルクォートとなる:

 
$ awk "BEGIN { print \"Here is a single quote <'>\" }"
-| Here is a single quote <'>

awk プログラムではダブルクォートやバックスラッシュ、ドル記号 はよく使われるものであるので、この選択肢もまた苦痛を伴うものである。

3 番目の選択肢は 8 進数のエスケープシーケンス相当をシングルクォート文 字やダブルクォート文字として使うもので、以下のようなものである:

 
$ awk 'BEGIN { print "Here is a single quote <\47>" }'
-| Here is a single quote <'>
$ awk 'BEGIN { print "Here is a double quote <\42>" }'
-| Here is a double quote <">

エスケープの意味するところを明確にコメントする限りはこれはうまく動作す る。

4 番目の選択肢はコマンドライン変数指定を用いるものであり、以下のような ものである:

 
$ awk -v sq="'" 'BEGIN { print "Here is a single quote <" sq ">" }'
-| Here is a single quote <'>

もしシングルクォートとダブルクォートの両方を awk プログラム で用いる必要があるのであれば、shell がプログラムの一部を shell のもの として解釈しないように、分割したファイルにしてしまうのが最良であり、そ うすることで正しくプログラムに伝えることができる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.2 例のための データファイル

このWeb ページ での例の多くは、二つのサンプルデータファイル から入力を取る。 一番目は `BBS-list'と呼ばれるものであり、 BBSのリストとその情報が記述されているファイルである。 二番目のデータファイル は`inventory-shipped' と呼ばれるもので 月毎の船積みの量が記述されているファイルである。 これらのファイルの各行には、一つのレコード(record)だけが置かれている。

`BBS-list'中の各レコードは、BBSの名称とその電話番号、ボーレート、 そして運営時間を表すコードから構成される。 最終カラムの `A'はBBSが終日運営されていることを示し、 同様に `B'は BBSの運営が夜間と週末のみであることを、 `C'は週末のみの運営である事を示す。

 
aardvark     555-5553     1200/300          B
alpo-net     555-3412     2400/1200/300     A
barfly       555-7685     1200/300          A
bites        555-1675     2400/1200/300     A
camelot      555-0542     300               C
core         555-2912     1200/300          C
fooey        555-1234     2400/1200/300     B
foot         555-6699     1200/300          B
macfoo       555-6480     1200/300          A
sdace        555-3430     2400/1200/300     A
sabafoo      555-2127     1200/300          C

二番目のデータファイル は`inventory-shipped'と呼ばれるもので、 年間の積込量を記述したものである。 各レコードは、月と、緑色のかごの積込量、赤い箱の積込量、 オレンジ色のバッグの積込量、そして青い包みの積込量から構成されている。 ファイルには16のエントリがあり、ある年の12ヶ月と、 その翌年の4ヶ月分のデータが記述されている。

 
Jan  13  25  15 115
Feb  15  32  24 226
Mar  15  24  34 228
Apr  31  52  63 420
May  16  34  29 208
Jun  31  42  75 492
Jul  24  34  67 436
Aug  15  34  47 316
Sep  13  55  37 277
Oct  29  54  68 525
Nov  20  87  82 577
Dec  17  35  61 401

Jan  21  36  64 620
Feb  26  58  80 652
Mar  24  75  70 495
Apr  21  70  74 514

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.3 幾つかの単純な例

以下に例示したコマンドは`BBS-list'という入力ファイルから `foo'という文字列を検索する単純なawkプログラムである。

 
awk '/foo/ { print $0 }' BBS-list

`foo'を含んでいる行が見つかったとき、その行は出力される。 なぜなら`print $0'がカレント行の出力を意味しているからである (`print'だけでも同じ意味になるのでそう書くこともできる)。

スラッシュ(`/')が文字列`foo'の周りにあることが 気になっただろう。このスラッシュは`foo'が検索対象のパターンで あることを示すものである。このタイプのパターンは正規表現 (regular expression)と呼ばれるもので、詳しくは後で(see section 正規表現) 説明する。 パターンは単語の一部分にマッチするものでも良い。 awkプログラムの周りにはシングルクォートがあるので シェルはシェルの特殊キャラクタを解釈することはない。

以下はこのプログラムの出力したものである。

 
$ awk '/foo/ { print $0 }' BBS-list
-| fooey        555-1234     2400/1200/300     B
-| foot         555-6699     1200/300          B
-| macfoo       555-6480     1200/300          A
-| sabafoo      555-2127     1200/300          C

awkのルールでは、パターンかアクションのいずれかを 省略することができるが両方を一度に省略することはできない。 パターンが省略された場合、そのアクションはすべての 入力行に対して適用される。 アクションが省略された場合のデフォルトのアクションは パターンにマッチしたすべての行を出力するというものである。

したがって、先の例ではアクション(print文とカーリーブレース)を 省略することができ、その場合でも同じ結果、つまりパターン`foo'に マッチしたすべての行が出力される。 対照的に、print文を省略したがカーリーブレースを残した場合には 空アクションとなりなにもしない(つまり何も出力されない)。

多くの有用なawkプログラムは短く、一行か二行のものである。 ここに集めたのはそういった、便利で短いプログラムである。 これらのプログラムのうちの幾つかは、まだ説明していない機能を使っている (プログラムの説明は何かをするための良いアイデアをあなたにもたらすだろうが、 どうかこのWeb ページ の残りを読んで欲しい。あなたがawkのエキスパートとなるために!)。 例の大部分は`data'という名前のデータファイル を使っている。 これは単にプレースホルダーである。これらのプログラムを実際に使うなら、 `data'をあなたのファイル名 で置き換えること。 将来のリファレンスのため、awkでの複数のやり方が載っている。 幾つかのポイントでは、これらの例を見るために後戻りして ここで例示されていることを行う異なるやり方を見出すかもしれない。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.4 二つのルールの例

awkユーティリティは一度のファイル入力で一行だけ入力する。 入力された個々の行に対して、awkプログラムで記述されている 各ルールのパターンとの照合を行う。 パターンとマッチすればアクションが実行され、 マッチするパターンがなければ何も実行されない。

すべてのルールを処理した後でawkは次の入力行の読み込みを行う (しかし例外はある。see section next、see section Using gawk's nextfile Statement)。 この動作はファイルの終端に達するまで繰り返される。

 
/12/  { print $0 }
/21/  { print $0 }

このawkプログラムは二つのルールから構成されている。 一つ目のルールは`12'という文字列を持ち、 アクションは`print $0'である。 二番目のルールは`21'という文字列を持ち、アクションとして `print $0'を持っている。各ルールのアクションはそれぞれ ブレースのペアで囲まれている。

この awk プログラムは、文字`12'か文字列`21'を 含むすべての行を出力する。 もし行の中に両方ともあれば、 その行はそれぞれのルール毎に出力が行われるので結果として二回出力されることになる。

このプログラムを`BBS-list'`inventory-shipped' の二つのサンプルデータファイルに対して実行した場合の結果はこうなる

 
$ awk '/12/ { print $0 }
>      /21/ { print $0 }' BBS-list inventory-shipped
-| aardvark     555-5553     1200/300          B
-| alpo-net     555-3412     2400/1200/300     A
-| barfly       555-7685     1200/300          A
-| bites        555-1675     2400/1200/300     A
-| core         555-2912     1200/300          C
-| fooey        555-1234     2400/1200/300     B
-| foot         555-6699     1200/300          B
-| macfoo       555-6480     1200/300          A
-| sdace        555-3430     2400/1200/300     A
-| sabafoo      555-2127     1200/300          C
-| sabafoo      555-2127     1200/300          C
-| Jan  21  36  64 620
-| Apr  21  70  74 514

`BBS-list'の中の`sabafoo'で始まる行が二回出力されている ことに注目。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.5 より複雑な例

以下の例は、あなたにawkプログラムの行うことの 典型的なアイデアを与えるためのものである。 この例はawkに対して、他のユーティリティの出力を要約し、 選択し、アレンジするための方法を示している。 まだ説明がされていない機能が使われているが、 (スクリプトの)全てが理解できなくても心配することはない。

 
ls -l | awk '$6 == "Nov" { sum += $5 }
             END { print sum }'

このコマンドはカレントディレクトリにある11月(年は何年でもよい) に最後の修正がなされたファイルの大きさの合計バイト数を出力する。 (9) 例の`ls -l' の部分は、あるディレクトリ中のファイルをそのサイズと 最終修正日付とともにリストアウトするためのコマンドであり、 その出力は以下のような形である。

 
-rw-r--r--  1 arnold   user   1933 Nov  7 13:05 Makefile
-rw-r--r--  1 arnold   user  10809 Nov  7 13:03 awk.h
-rw-r--r--  1 arnold   user    983 Apr 13 12:14 awk.tab.h
-rw-r--r--  1 arnold   user  31869 Jun 15 12:20 awkgram.y
-rw-r--r--  1 arnold   user  22414 Nov  7 13:03 awk1.c
-rw-r--r--  1 arnold   user  37455 Nov  7 13:03 awk2.c
-rw-r--r--  1 arnold   user  27511 Dec  9 13:07 awk3.c
-rw-r--r--  1 arnold   user   7989 Nov  7 13:03 awk4.c

最初のフィールドは読み書きの許可フラグ、 二番目のフィールドはそのファイルへリンクしているリンクの数、 三番目のフィールドはそのファイルのオーナーのID、四番目がファイルのグループ、 五番目がそのファイルの大きさをバイトで表したもの、 六,七,八番目のフィールドは最後にそのファイルが修正された月、日、時間である。 最後の九番目のフィールドはファイルの名前である。 (10)

このawkプログラムの`$6 == "Nov"'という式は、 `ls -l'の出力の六番目のフィールドが`Nov'という文字列と マッチするかどうかをテストしている。 `Nov'という文字列を六番目のフィールドに持つ行が読み込まれるたびに ` sum += $5 'というアクションが実行される。 このアクションでは、五番目のフィールド(ファイルサイズ)をsum という変数に足し込んでいる。結果として、 sumはパターンにマッチした行のファイルの大きさの合計を表す (awkの変数は、自動的に 0で初期化されている)。

ls の出力から渡される最後の行が処理された後で ENDルールが実行され、 sumの値が出力される。 この例では、 sumの値は80600になるだろう。

こういった類の高度なawkの使い方は後の方のセクションで述べられている (see section アクション)けれども、 その様な使い方を覚える前にどのように入力が解釈され、 どのように出力が表示されるかを知ったほうがよいだろう。 フィールドを操作し、print文を使うことによって、 有用なそして華やかな見栄えのするレポートを作成することができる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.6 awk Statements Versus Lines

ほとんどの場合、awkプログラムの各行は次の例にみられるように独立した文、 あるいは独立したルールである。

 
awk '/12/  { print $0 }
     /21/  { print $0 }' BBS-list inventory-shipped

しかし、gawkは以下に挙げるものに続く改行は無視する。

 
,    {    ?    :    ||    &&    do    else

その他の場所にある改行は文の終端として認識される。 (11)

一つの文を二つの行に改行で分けたいときに、 行末にバックスラッシュ`\'(訳注: 日本語環境では円記号) を置くことによって行を継続することができる。 このバックスラッシュによる行の継続はどこにでも置くことができる。 たとえ文字列や正規表現の途中であっても可能である。例えば

 
awk '/This regular expression is too long, so continue it\
 on the next line/ { print $1 }'

このWeb ページ にあるサンプルプログラムでは、 通常はバックスラッシュによる継続を使用しない。 なぜなら、gawkには一行の長さに制限がなく 継続の必要が全くないからである。 また、継続を使わないことによってプログラムがより読みやすくなるということもある。 同じ理由によって、サンプルプログラムではほとんどの文を短く保っている。 バックスラッシュによる継続は、あなたのawkプログラムがコマンドライン上で タイプされるものの代わりに分割されたソースファイルであるときに非常に便利である。 ここで、バックスラッシュによる行継続は各awk処理系の実装に依存するもので あることに注意するべきである。たとえば、 文字列定数を行継続を使って分割することを許していない処理系がある。 したがって、あなたの作成するawkプログラムの移植性を最大限に保つためには、 正規表現や文字列の途中での行の分割を行わないことが最良の手段である。

警告: バックスラッシュによる行の継続は Cシェルではこのマニュアルに書かれている通りには動作しない。 バックスラッシュによる行継続はファイルに記述された awkプログラムや Bourne シェル、 Bash(GNU Bourne-Again shell)等のPOSIX に従ったシェルでの one-shot programsでは働くが、 Cシェル(csh)ではそうではない! Cシェルの場合はバックスラッシュを改行の前に二つ続けて書かなければならない。 同様に、Cシェルを使っているとき には awkプログラムのすべての行の改行をバックスラッシュで エスケープしなければならないことに気をつけること。例を挙げよう。

 
% awk 'BEGIN { \
?   print \\
?       "hello, world" \
? }'
-| hello, world

ここで、`%'`?'はそれぞれCシェルの一次プロンプトと二次プロンプトである (標準シェルでは `$'`>')。

POSIX準拠のシェルではどうなるか対比してみよう

 
$ awk 'BEGIN {
>   print \
>       "hello, world"
> }'
-| hello, world

awkは行指向の言語である。 各々のルールのアクションはパターンとして同じ行に置かねばならない。 パターンとアクションを分割した行に置くには、 バックスラッシュによる行継続を使わなければならない

バックスラッシュによる継続とコメントとを混ぜないように気をつけよう。 awk`#'を見つけるやいなやコメントが始るとみなし、 そこから、行の後のほうはすべて無視する。例を挙げる。

 
$ gawk 'BEGIN { print "dont panic" # a friendly \
>                                    BEGIN rule
> }'
error--> gawk: cmd. line:2:                BEGIN rule
error--> gawk: cmd. line:2:                ^ parse error

In this case, it looks like the backslash would continue the comment onto the next line. However, the backslash-newline combination is never even noticed because it is "hidden" inside the comment. Thus, the BEGIN is noted as a syntax error. ここで、バックスラッシュはコメントを次の行に継続しているようにみえる。 どんなやり方でも、バックスラッシュと改行の組み合わせは決して (awkには) 気づかれない。 なぜなら、それ(バックスラッシュ)が、コメントの内部に“隠れた”からである。 したがって、BEGINは文法エラーとして指摘される。

一つのルールの中にあるawkの文が短いときには、 それを一行にいくつもまとめて書きたいと考えるだろう。 そのためにはセミコロン`;'を使って文を区切る。 これはルールそれ自身にも適用できる。したがって、 このセクション の最初にあった例はこのように書くこともできる。

 
/12/ { print $0 } ; /21/ { print $0 }

注意 : オリジナルのawk言語では、 同じ行にあるルールをセミコロンで区切らなくても良かった。 これ(セミコロンでルールを区切ること)はアクション中にあるステートメントの 扱いとの一貫性をとるために付け加えられたものである。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.7 awk のその他の機能

awkは、作成したプログラムがawkから情報を得るための 組込みであらかじめ定義されている幾つかの変数を提供している。 その他にも、プログラム中でawkがどのようにデータを処理するかを 制御するための変数がある。

さらに、awkは一般的な計算や文字列操作をおこなうための組込み関数を提供している。 gawkはタイムスタンプ、ビット操作、実行時の文字列変換を 行うための組み込み関数を提供している。

我々はawk言語を開発するときに、変数の大部分と 多くの関数を導入した。それらは組み込み変数組み込み関数に システマチックに定義されている。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.8 awk を使うとき

(See section より複雑な例.) あなたは、awkがあなたにとって役に立つのかどうかを 疑問に思うかもしれない。ユーティリティプログラムやパターン、 フィールドの分割、算術文、などの選択基準を使って、 より複雑な出力を作成することができる。 awk言語はls のようなほかのユーティリティの出力からの情報を要約するような、 大きな生データからレポートを生成するような作業に便利である。

awkで書かれたプログラムは、通常は他の言語で書いたものよりも小さくなる。 これはawkプログラムの作成と使用を簡単にする。 しばしば、awkプログラムはターミナルで即座に作成し、 一度だけ使って捨ててしま うということもできる。 awkプログラムはインタープリットされるために、 エディット-コンパイル-テスト-デバッグというソフトウェア開発の 典型的なサイクルを避けることができるのである。

完全にretargetableな8ビットマイクロプロセッサ用のアセンブラ (詳しくはsee section 用語集)や、 特殊目的用Prologコンピュータ用のマイクロコードアセンブラのような 複雑なプログラムがawkを使って記述されたことがある。 最近では、gawkがWikiクローンを記述するのに用いられた(12)。オリジナルのawkの能力はそのような複雑なことをこなすには 機能が制限されていたが、最近のバージョンではより多機能になっている。 Bell研バージョンのawkでさへ幾つかのあらかじめ決められた 制限があるが以前のものよりゆるいものになっているのである。

2、300行以上のawkスクリプトを書くようになったとき、 違ったプログラミング言語の使用を考えていることに気がつくだろう。 Emacs Lisp は、文字列やパターンマッチングを扱う高度な能力を必要とするときには よい選択といえる。シェルもまた文字列やパターンマッチングを扱う能力があり、 それに加えて強力で便利なシステムユーティリティを使う事ができる。 より伝統的な言語、CやC++、Javaといったものは、シ ステムプログラミングや大きなプログラムの複雑さを管理するのに便利であるだろう。 これらの言語で記述したプログラムは、同じ作業をする awk プログラムよりも 多くの行数を必要とするかも知れないが、より有効に実行したり、簡単にメンテナンスできる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2. 正規表現

正規表現(regular expression または regexp)とは、文字列の集合を表現する方法である。 正規表現はawkプログラミングにおいて非常に基本的な部分であるので、 その書式と使い方は章 を分けて説明するに値する。

スラッシュ(`/')に囲まれた正規表現は、 その正規表現が示す集合に属するテキストが含まれる全ての入力レコードにマッチする awkのパターンである。 もっとも単純な正規表現は、文字や数字、もしくはその両方の並びである。 このような正規表現はそのような並びを含む任意の文字列にマッチする。 したがって、`foo'という正規表現は`foo'を含む任意の文字列に マッチすることになる。 それにより、/foo/というパターンはレコードのどこであっても `foo'という三文字を含む入力レコードにマッチすることになるのである。 他の種類の正規表現は、あなたがより複雑な文字列の集合を指定できるようにするものである。

Initially, the examples in this 章 are simple. As we explain more about how regular expressions work, we will present more complicated instances.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2.1 正規表現の使い方

正規表現はスラッシュで囲まれたパターンとして使うことができる。 そのような正規表現は各レコードのテキスト全体に対してのテストが行われる (通常は、成功かどうかを決めるにはテキストの一部分だけ見れば良い)。 以下に挙げる例は`foo'を含むレコードの第二フィールドを出力する。

 
$ awk '/foo/ { print $2 }' BBS-list
-| 555-1234
-| 555-6699
-| 555-6480
-| 555-2127

~ (tilde), ~ operator 正規表現はまた、マッチング式として使うことができる。 これらの式はマッチングを試みる文字列を特定することを許し、 カレント入力レコード全体とマッチングすることをしないで済むようにできる。 `~'`!~'という二つの演算子は正規表現の比較をおこなう。 これらの演算子を使った式はパターンとしてや使ったり、 ifwhilefordo文中で使うこととができる (See section アクション中の制御文)。

 
exp ~ /regexp/

これはexp(文字として扱われる)という式がregexp とマッチするときに真となる。 以下に挙げる例は、第一フィールドに大文字の`J' を含んでいる入力レコードをすべて選択するものである。

 
$ awk '$1 ~ /J/' inventory-shipped
-| Jan  13  25  15 115
-| Jun  31  42  75 492
-| Jul  24  34  67 436
-| Jan  21  36  64 620

こう書いても良い。

 
awk '{ if ($1 ~ /J/) print }' inventory-shipped

以下の例はexp(文字列として扱われる)という式が regexpマッチしないときに真となる。 次に挙げる例では、最初のフィールドに大文字の `J'を含まない入力レコードすべてを選択する。

 
exp !~ /regexp/

以下の例は、すべての入力レコードのうち 第一フィールドに大文字の`J'含んでいないものを 取り出す。

 
$ awk '$1 !~ /J/' inventory-shipped
-| Feb  15  32  24 226
-| Mar  15  24  34 228
-| Apr  31  52  63 420
-| May  16  34  29 208
…

正規表現が/foo/のようにスラッシュで囲まれて記述されたとき、 これを正規表現定数(regexp constant)と呼ぶ。 これは5.27のような数値定数や "foo"のような文字列定数のようなものである。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2.2 エスケープシーケンス

一部のキャラクタは、"foo"のような文字列定数や /foo/のような正規表現定数にそのままの形で含める事ができない。 このようなキャラクタは、キャラクタそのものではなく バックスラッシュ(`\'で始 まるエスケープシーケンス)を 代わりに使うことで文字列などに含めることができる。 エスケープシーケンスの一つの用途は文字列定数に二重引用符を含めるためである。 生の二重引用符は文字列の終端になってしまうため、 それが二重引用符のキャラクタそのものであることを明示するために `\"'を使わなければならない。例を挙げよう。

 
$ awk 'BEGIN { print "He said \"hi!\" to her." }'
-| He said "hi!" to her.

バックスラッシュキャラクタそれ自身は、そのまま含むことのできないキャラクタである。 文字列や正規表現中にバックスラッシュを一つ含めるには、 `\\' と記述する。したがって、 `"'`\'という二つのキャラクタからなる文字列は "\"\\"と記述しなければならない。

もう一つのバックスラッシュの用途は、タブとか改行のような非印字キャラクタ (unprintable characters)を表すためのものである。 非印字キャラクタをそのまま文字列定数や正規表現定数に含めてしまうと 多分見づらくなってしまう。

以下にawkで使われる全てのエスケープシーケンスを挙げる。 特に断りがない限り、これらのエスケープシーケンス全ては文字列定数と 正規表現定数の両方に適用される。

\\

`\'そのもの。

\a

"警告"キャラクター。Ctrl -gであり、ASCIIのcode 7 (BEL) (通常は何らかの音が出る')。

\b

後退。Ctrl -hであり、ASCII の code 8 (BS)

\f

改ページ。Ctrl -lであり、ASCII の code 12 (FF)

\n

改行。Ctrl -jであり、ASCII の code 10 (LF)

\r

復帰。Ctrl -mであり、ASCII の code 13 (CR)

\t

水平タブ。Ctrl -iであり、ASCII の code 9 (HT)

\v

Vertical tab, Ctrl -k, ASCII code 11 (VT). 垂直タブ。Ctrl -kであり、ASCII の code 11 (VT)

\nnn

八進数値nnnの値。nnn`0'から`7'までの数字の一つから三つの並び。 ASCIIのESC(エスケープ)キャラクタのコードは `\033'である。

\xhh

十六進数値hhの値。 hhは十六進文字(`0'から`9' までと、 `A'から`F'もしくは`a'から`f')。 ISO Cと同様に、最初の非十六進文字が見つかるまでエスケープシーケンスは 連続しているものとみなされる。しかしながら、 二文字を越える十六進文字列はどのような結果になるかは未定義である (`\x'エスケープシーケンスはPOSIXのawkでは許されていない)。

\/

文字としてのスラッシュ(正規表現定数に対してのみ必要)。 これはスラッシュを正規表現定数に含めたいときに使用する。 正規表現がスラッシュで終端するので、 awkに残りの正規表現を処理し続けるのを指示するのに エスケープしてスラッシュをパターンの一部にする必要がある。

\"

二重引用符そのもの(文字列定数に対してのみ必要)。 これは二重引用符を文字列定数に含めたいときに使用する。 文字列が二重引用符で終端するので、文字列の残りを awkに処理し続けるのを指示するのに、 エスケープしてダブルクォートを文字列の一部にする必要がある。

gawkでは、正規表現中で特別な意味を持つバックスラッシュで始まる 二文字のエスケープシーケンスが幾つか追加されている。 See section gawk固有の正規表現演算子.

正規表現では、gawk固有の正規表現演算子のリストにも先のリストにもない バックスラッシュが先行したキャラクタは それが通常は正規表現演算子であるとしても リテラルとして扱われる。 たとえば、/a\+b/は三つのキャラクタの並び`a+b'にマッチする。

完全な互換性を保つため、先のリストにないキャラクタの前にバックスラッシュを 置かないこと。

まとめ

Advanced Notes: Backslash Before Regular Characters

もしバックスラッシュを先のリストにはないキャラクタの前に置いた場合、 POSIX のawkではその場合の動作は未定義である。 このとき二つの選択肢がある。

Strip the backslash out
バックスラッシュを取り除く。

これはUnixのawkとgawkの両方が行っている。 たとえば、"a\qc" は "aqc"と等しい (これは安易なバグを持ち込みやすく、間違いやすいのでgawkは 警告する)。 フィールドセパレータとして空白に囲まれた垂直バーを使おうとして `FS = "[ \t]+\|[ \t]+"'としたときを考えてみよう。 これは`FS = "[ \t]+\\|[ \t]+"'と二つのバックスラッシュを 使うべきである。

バックスラッシュをそのままにしておく

その他の一部のawkの実装でこれが使われている。 この場合、"a\qc"a\\qc"としたときと同じになる。

Advanced Notes: メタキャラクタのエスケープシーケンス

八進や十六進のエスケープを使用して正規表現のメタキャラクタを表すシーケンスを記述したとき (正規表現演算子)、 awkはそのようなキャラクタを文字として見るのだろうか? それとも正規表現の演算子と見るのだろうか?

このようなキャラクタは伝統的にその文字そのものとして扱われる。 (d.c.) しかし、POSIX標準ではこれはメタキャラクタであるかのように扱うよう指示されており、 gawkの振る舞いもそうなっている。 ただし互換モード (see section コマンドラインオプション)では、 gawkは正規表現定数中で八進や十六進のエスケープシーケンスによって 表現されたキャラクタは文字そのものとして扱われる。 したがって、/a\52b//a\*b/と等価である。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2.3 正規表現演算子

以下に挙げるような正規表現演算子(regular expression operators) とかメタキャラクタ(metacharacters) と呼ばれる正規表現の記述力を増加させるようなキャラクタを使って、 正規表現式を組み合わせることができる。

エスケープシーケンスで 説明されているエスケープシーケンスは正規表現の中でも使うことができる。 それらは先頭に`\'が置かれている。 正規表現の処理に先立ってこのようなエスケープシーケンスは解析されて、 そのシーケンスの表すキャラクタに置きかえられる。

以下はメタキャラクタの一覧である。 エスケープシーケンスではなくかつこの一覧にないキャラクタはすべて、 それ自身を表すキャラクタである。

\

これはマッチングのときにあるキャラクタの特殊な意味を抑制するために使用する。 例えば、`\$'`$'というキャラクタにマッチする。

^

これは文字列の先頭にマッチする。例えば、`^@chapter' は文字列の先頭にある`@chapter'にマッチする。 これは Texinfoファイルで章の先頭を見つけるのに使用できる。 `^'は文字列の先頭部分にのみマッチさせるのに使う目印であるので、 アンカー(anchor)として知られている。

`^'は 文字列中にある行(line embedded in a string)の先頭にはマッチしないことに注意。 次の例の条件式は真にはならない。

 
if ("line1\nLINE 2" ~ /^L/) …
$

これは`^'に似ているが、文字列の最後にマッチする。例えば、 `p$'`p'で終わるレコードにマッチする。 `$'もアンカーであり、文字列中の行末にはマッチしない。例えば、

 
if ("line1\nLINE 2" ~ /1$/) …

この条件式は真にはならない。

.

ピリオド、あるいはドットは改行を含む任意のキャラクタにマッチする。 例えば、`.P'`P'が後に続く任意の一文字にマッチする。 連接を使うことによって、`U.A'のような `U'で始まり`A'で終わる任意の三文字の並びにマッチするような 正規表現を作ることができる。

厳密なPOSIXモード(see section コマンドラインオプション)では、 `.'はすべてのビットが0であるキャラクタ、NULにはマッチしない。 しかしながら、NULは単にもう一つのキャラクタである。 他のバージョンのawkでは、 NULキャラクタにはマッチさせることができない。

[…]

これはキャラクタリスト(character list)と呼ばれるものである (13)。 これはブラケットに囲まれているキャラクタのいずれか一つとマッチする。 例えば、`[MVX]' は文字列中にある`M', `V', `X'のいずれかの キャラクタとマッチする。 キャラクタリストの使用にブラケットの内側で使うことのできるものの 詳細な説明がある。

[^ …]

これはキャラクタリストの補集合(complmented character list)である。 `['の直後のキャラクタは `^'でなければならない。 これはブラケットの中にあるキャラクタ以外の任意のキャラクタにマッチする。 たとえば`[^awk]'`a'`w'`k'以外のキャラクタに マッチする。

|

これは選択演算子(alternation operator)であり、選択を行うのに使用する。 `|'は正規表現演算子の中で最も低い優先順位を持つ。 たとえば、`^P|[[:digit:]]'`^P'`[[:digit:]]'の いずれかにマッチする。つまり、`P'で始まるものか 数字を含むものにマッチする。

選択はその両辺の可能な限り大きな部分式に対して適用される。

(…)

カッコは正規表現を(算術式のときと同じ様に)グループとする為に使用される。 これは選択演算子`|'を含んだ正規表現を連結する為に使う事ができる。 たとえば、`@(samp|code)\{[^}]+\}'`@code{foo}'にも`@samp{bar}'にもマッチする (これはTexinfoのフォーマット制御シーケンスである。`+'は 後述する)。

*

このシンボルはその前に置かれている正規表現の必要な限りのくり返しにマッチする。 例えば `ph*'では`*'はその前にある`h'に適用され、 一文字の`p'に続いて任意の数の`h'がある文字列にマッチする。 これは`p'だけで`h'が一個もないようなパターンにもマッチする。

`*'のくり返しは可能なかぎり最も小さな式が採用される (もしより大きな式を繰り返したいのならば括弧を使えばよい) 。 そしてその式は可能な限り大きなくり返しを見つけだす。例えば、 `awk '/\(c[ad][ad]*r x\)/ { print }' sample'`sample'中のレコードのうち、 `(car x)', `(cdr x)', `(cadr x)' のような文字列を含むものを全て出力する。 カッコの前にバックスラッシュをつけてエスケープしていることに注意。

+

このシンボルは`*'と似ているが、 先行する部分正規表現が少なくとも一回マッチしなければならない。つまり、 `wh+y' でいうと、`wh*y'ではすべてマッチしていた `why'`whhy'`wy'のうち、前者二者にはマッチするが、 最後のものにはマッチしないということである。 次の例は、`*'で最後に挙げた例を書き直したものである。

 
awk '/\(c[ad]+r x\)/ { print }' sample
?

このシンボルは`*'と似ているが、先行する部分正規表現がないか、 一回だけのときにマッチする。例えば `fe?d'`fed'`fd'にマッチするが、それ以外にはマッチしない。

{n}
{n,}
{n,m}

interval expresionのブレースの内側には、一つか二つの数値がある。 もしブレースの中に数値が一つだけならば、直前の正規表現がn回繰り返される。 もしカンマに区切られ て数値が二つあれば、直前の正規表現が n回からm回繰り返される。カンマの後ろに数値が一つだけならば、 直前の正規表現が少なくともn回繰り返される。

wh{3}y

これは`whhhy' にマッチするが、 `why'`whhhhy'にはマッチしない。

wh{3,5}y

これは `whhhy'`whhhhy'`whhhhhy'のいずれかのみにマッチする。

wh{2,}y

`whhy'`whhhy'などにマッチする。

POSIX標準の一部として、 awkegrepとほかのユーティリティとの一貫性のためこれが追加された。

しかしながら、古いプログラムでは`{'`}'を正規表現定数の中で 使っているかもしれないので、 gawkはデフォルトでは正規表現中の interval expressionsはマッチしない。 `--posix'オプションか `--re-interval'オプション (see section コマンドラインオプション) が指定されると、 interval expressionsを正規表現として使うことができるようになる。

正規表現定数で`{'`}'を使った新しいプログラムでは、 それらを常にバックスラッシュでエスケープすると良いだろう。 こうすることによりその正規表現定数は どのバージョンのawkを使っても あなたの望んだとおりに動くようになる。 (14)

正規表現では、`*', `+', `?'といった演算子は `{'`}'と同じ優先順位を持ち、最も高い。 続いて連接があり、最後に`|'がある。 算術式と同じように、カッコはそれで括った演算子の優先順位を変更することができる。

POSIXのawkgawkでは、`*'`+'`?' といった演算子はそれに先行する正規表現がない場合にはそれ自身を表す。 たとえば`/+/'はリテラルの`+'にマッチする。 しかしながら、他の多くのawk処理系ではこれは構文エラーとみなされる。

gawkが互換モード(see section コマンドラインオプション)で動作している場合には、 POSIXキャラクタクラスとinterval式は正規表現中で使うことができなくなる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2.4 キャラクタリストの使用

キャラクタリストの中では、範囲式(range expression)はハイフンで区切られた 二つのキャラクタからなる。これは 現在のロカールの照合シーケンスとキャラクタセットを使って、 二つのキャラクタの間に存在する任意の1キャラクタにマッチする。 たとえば、デフォルトのC ロカールでは、`[a-dx-z]'`[abcdxyz]' と等価である。多くのロカールはキャラクタを辞書順にソートしているので、 そういったロカールでは`[a-dx-z]'`[abcdxyz]'と等価ではなくなる。 その代わりにたとえば `[aBbCcDdxXyYz]' と等価になる。伝統的な ブラケット式の解釈を得るために、環境変数 LC_ALLに値 `C'を設定する ことによってC ロカールを使うことができる。

`\', `]', `-', `^' といったキャラクタをキャラクタリスト 中に置くには、その前に`\'を置く。例を挙げよう:

 
[d\]]

これは `d'`]'にマッチする。

キャラクタリスト中の `\' の扱いは他の awk の実装や POSIX の要求に従ったものである。awk の正規表現は POSIX の定義する拡張正規表現(Extended Regular Expressions、 EREs)の スーパーセットである。POSIX の拡張正規表現は伝統的な egrep ユーティリティが受け付ける正規表現に基づいている。

キャラクタクラス は POSIX 標準によって導入された新しい機能である。 キャラクタクラスは特別な属性を持つキャラクタリストを表す特殊な記法であるが、 実際のキャラクタは国ごと、またはキャラクタセットごとに異なるものとなる 可能性がある。たとえば、アルファベットは米国とフランスでは異なるものである。

キャラクタクラスはキャラクタリストのブラケットの内側でのみ正当である。 キャラクタクラスは`[:'、クラスを表すキーワード、`:]'から 構成される。table-char-classesは POSIX 標準で定義されるキャラクタクラスのリストである。

Class

Meaning

[:alnum:]

アルファベットまたは数字。

[:alpha:]

アルファベット。

[:blank:]

スペースまたはタブ。

[:cntrl:]

制御文字。

[:digit:]

数字。

[:graph:]

印字可能かつ可視のキャラクタ。 (スペースは印字可能であるが可視ではない。`a' は印字可能かつ可視である)

[:lower:]

アルファベットの小文字。

[:print:]

印字可能なキャラクタ (制御文字でないキャラクタ)。

[:punct:]

Punctuation characters (文字、数字、制御文字、スペースでないキャラクタ)。

[:space:]

空白 (スペース、タブ、改ページ)。

[:upper:]

アルファベットの大文字。

[:xdigit:]

十六進の数字。

Table 2.1: POSIX Character Classes

たとえば、POSIX 標準以前は、アルファベットと数字にマッチさせるのに /[A-Za-z0-9]/と記述する必要があった。もし別のアルファベットが あった場合にはそれはマッチしなかったし、ASCII ではないキャラクタセット を照合している場合には ASCII の alphanumeric なキャラクタにマッチしなかった かもしれない。POSIX のキャラクタクラスを使うことによって、 使用しているキャラクタセットにおけるアルファベットとキャラクタにマッチする ものとして /[[:alnum:]]/と記述することができる。

さらにキャラクタリスト中に現れる二つの特殊なシーケンスがある。 これらは非ASCIIキャラクタセットに適用され、二文字以上で表される 単一のシンボル(照合要素 collating elements)を持つことができる。 これらはまた照合や並び替えのための等価なキャラクタを持つ (たとえばフランス語では、生の "e" と grave-accent のついた "è" は 等価である)。 これらのシーケンスは以下のとおり:

照合要素(Collating symbols)

[ch] is a regexp that matches either `c' or `h'. 複数キャラクタの照合要素は`[.'`.]' に囲まれる。 たとえば`ch' が照合要素であった場合、[[.ch.]] は この照合要素にマッチする正規表現であるのに対して [ch]`c'もしくは`h'にマッチする正規表現である。

等価クラス(Equivalence classes)

キャラクタの等価なもののリストのためのロカール固有の名前である。この名前は `[='`=]' で囲まれる。たとえば、`e' という名前は "e"、 "è" 、"é" のすべてを表すのに使われるかもしれない。 この場合、[[=e=]]`e', `é', `è' のいずれかにマッチする 正規表現である。

これらの機能は非英語圏のロカールにおいて非常に便利である。

警告: gawkが使っている正規表現マッチングのためのライブラリ関数は 現在のところPOSIXのキャラクタクラスのみを認識し、照合要素や等価クラスは認識しない。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2.5 gawk固有の正規表現演算子

正規表現を扱う GNU ソフトウェアは多くの正規表現演算子を追加している。 これらの演算子はこのセクション で説明され、gawkに 固有のものである。これらの演算子は他の awk 処理系では 使うことができない。追加された演算子のほとんどは単語マッチングを 扱うものである。ここで、単語とは、一つ以上の文字、数字、 アンダースコア(`_')の並びである:

\w

任意の単語を構成するキャラクタ -- つまり、任意の文字、 数字、アンダースコアにマッチする。[[:alnum:]_]の 短縮記法と考えるとよい。

\W

単語を構成しない任意のキャラクタにマッチする。 [^[:alnum:]_]の短縮記法。

\<

単語の先頭にある空文字列にマッチする。たとえば、/\<away/`away' にマッチするが `stowaway'にはマッチしない。

\>

単語の終端にある空文字列にマッチする。 たとえば、/stow\>/`stow' にマッチするが `stowaway' にはマッチしない。

\y

単語の先頭もしくは終端にある空文字列にマッチする。たとえば、 `\yballs?\y' は 独立した単語の `ball' もしくは `balls' に マッチする。

\B

単語を構成するキャラクタ二つの間の空文字列にマッチする。 /\Brat\B/`crate' にマッチするが `dirty rat' には マッチしない。`\B'`\y' の反対である。

バッファ上で働く二つの演算子がある。Emacs では、バッファは Emacs のバッファである。gawk の正規表現ライブラリルーチンは バッファとして文字列全体を扱う。二つの演算子は次の通り:

\`

バッファ(文字列)の先頭にある空文字列にマッチする。

\'

バッファ(文字列)の終端にある空文字列にマッチする。

`^'`$' は常に文字列の先頭や終端に対して働くので、 これらの演算子は何の機能もawkに追加しない。これらは 他の GNU ソフトウェアとの互換のために提供されている。

他の GNU ソフトウェアでは、単語境界の演算子は `\b' である。 しかしながら、これはバックスペーストして定義されている awk 言語と 衝突する。このため、gawk は別の文字を使っている。 別の選択肢としては、GNU の演算子には二つのバックスラッシュを要求するという やり方もあったが、これは混乱を招く。現在の、GNU の `\b'`\y' で代替するのはややましな方法である。

control how gawk interprets characters in regexps: gawk での正規表現中のキャラクタの解釈を制御する幾つかの コマンドラインオプションがある(see section コマンドラインオプション):

オプションなし

デフォルトでは、POSIX の正規表現と 先に説明した GNU の正規表現演算子 正規表現演算子で説明した GNU の正規表現演算子 が使える。 しかしながら、interval expressions はサポートされない。

--posix

POSIX の正規表現だけがサポートされる。GNU の演算子は特別な意味を 持たない(たとえば、`\w' がリテラルの `w'にマッチする)。 Interval expression を使うことができる。

--traditional

伝統的な Unix の awk の正規表現が使える。 GNU の演算子は特別な意味を持たず、interval expressions や POSIX のキャラクタクラス ([[:alnum:]]など)は使えない。 八進表記や十六進表記のエスケープシーケンスは、それが正規表現の メタキャラクタを表すものであってもリテラルとして扱われる。 また、gawk はコマンドライン上で名付けられたディレクトリを少しだけ省略できsる。

--re-interval

`--traditional' が指定された場合でもinterval expressionの使用を 許可する(`--posix' は自動的にinterval expressionsを有効にする。 このため、`--posix'が使われている場合には`--re-interval'は 余計である)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2.6 Case Sensitivity in Matching

正規表現における大小文字の違いは、通常のキャラクタ(メタキャラクタでないもの) とキャラクタセットのマッチング両方で区別される。 したがって、正規表現中の`w'は小文字の`w'にのみマッチし、 大文字の`W'にはマッチしない。

大小文字を区別しないマッチングの最も簡単な方法は、`[Ww]'のように キャラクタリストを使うというものである。しかしながら これは頻繁に使うときには扱いにくいものであり、正規表現を 読みづらいものにしてしまう。より良いであろう回避策が二つある。

プログラムの特定の場所で大小文字を区別しないマッチングを行う方法の一つが 対象となるデータの大小文字をtolowertoupperといった 組み込みの文字列関数(まだ説明していない。see section 文字列操作関数)を使って 正規化してしまうということである。たとえば

 
tolower($1) ~ /foo/  { … }

これは第一フィールドを、それに対してマッチングを行う前に小文字に 変換している。これはPOSIX準拠のawkで動作する。

もう一つの、gawkに固有のやり方は IGNORECASEという変数(see section 組み込み変数)にゼロ以外の値を セットするというものである。 IGNORECASEにゼロ以外がセットされたとき、すべての 正規表現演算及び文字列演算において大小文字の違いが無視される。 IGNORECASEの値の変更はプログラムの実行時に大小文字に 関する動作を動的に制御する。 IGNORECASEはほとんどの変数がそうであるように初期値がゼロであるので、 デフォルトでは大小文字の違いは有効である。

 
x = "aB"
if (x ~ /ab/) …   # このテストは失敗する

IGNORECASE = 1
if (x ~ /ab/) …   # 今度は成功する

一般的には、IGNORECASSEを一部のルールで大小文字の違いを 無視し、それ以外のルールでは違いを有効にするようにするために使うことは できない。なぜなら、IGNORECASEを特定のルールの パターンのためにセットする直接のやり方がないからである。 (15)

IGNORECASEは、コマンドラインやBEGINルールの中で 設定することができる(see section その他のコマンドライン引数、see section スタートアップとクリーンアップのアクション)。 コマンドラインからIGNORECASEを設定することは プログラムを編集することなく大小文字を無視するようにする方法である。

gawkの3.0より前では、IGNORECASEの値は 正規表現操作に対してのみ影響を及ぼしていて、 `=='`!='などの文字列比較には影響しなかった。 バージョン 3.0 から、正規表現と文字列比較の両方の操作に IGNORECASEが影響するようになった。

gawk 3.0から、大文字と小文字の間の等価性はISO-8859-1(ISO Latin-1) キャラクタセットに基づいたものになっている。このキャラクタセットは 伝統的な128のASCIIキャラクタセットのスーパーセットであり、ヨーロッパで 使われるキャラクタの多くを提供している。

gawk 3.1.4 では、大小文字の違いはロカールを意識するようになった。 これはisalpha()touuper()のような、 Cの<ctype.h>機能に基づくものである。

IGNORECASEの値はgawkが互換モード(see section コマンドラインオプション) のときは何の効果も及ぼさない。互換モードでは大小文字は常に区別される。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2.7 どのくらいテキストがマッチするか?

以下の例について考えてみよう

 
echo aaaabcd | awk '{ sub(/a+/, "<A>"); print }'

これはsub関数(まだこれは説明していない。see section 文字列操作関数) を使って入力レコードを変更する例である。 ここで、/a+/という正規表現は"一つ以上の`a'"を表しており、 それにマッチするテキストを``<A>'に置き換えるというものである。

入力には四つの`a'がある。出力はどうなるだろうか? awk(とPOSIX)の正規表現は、常にマッチするもののうち最も左にあり、 最も長いキャラクタの並びにマッチする (最左最長一致)。 したがって、 この例では四つの連続した`a'が(一つの)`<A>'に置き換えられる。

 
$ echo aaaabcd | awk '{ sub(/a+/, "<A>"); print }'
-| <A>bcd

単純にマッチする/しないをテストするときには今述べた事はさして重要ではない。 しかし、正規表現に基づいたフィールドやレコードの分割、 そしてmatch, sub, gsub, gensub といった関数 を使用してテキストのマッチングや置き換えをする場合には非常に重要である。 この原則を理解する事は正規表現に基づいたレコード/フィールドの分割のためにも重要である (see section 入力がどのようにレコードに分割されるかおよびsee section フィールドがどのように分割されるかを特定する)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2.8 動的正規表現を使う

`~'演算子や`!~'演算子の右辺に置くものは正規表現定数 (スラッシュの間に置かれた文字列)である必要はなく、任意の式を置くことができる。 そのような式は評価され、必要があれば文字列に変換される。 評価され変換が行なわれた結果は正規表現として扱われる。 このように演算が行われる正規表現は動的正規表現(dynamic regular expression)と呼ばれる。例を挙げよう。

 
BEGIN { digits_regexp = "[[:digit:]]+" }
$0 ~ digits_regexp    { print }

これはdigits_regexpに一つ以上の数字を表す正規表現をセットして それを入力レコードに対してマッチングしている。

警告: `~'演算子や`!~'演算子を使った場合、 スラッシュで囲まれた正規表現定数と二重引用符で囲まれた文字列定数とでは違いがある。 文字列定数を使おうとする場合、文字列が二度走査されることを理解する必要がある。 一度目はawkがプログラムを読み込んだときで、 二度目は演算子の左側に置かれた文字列と右側のパターンとのマッチングを行うときである。 これは式として評価される(先の例で言う digits_regexpのように) 任意の文字列において真であり、文字列定数そのものにはあてはまらない。

文字列が二度走査されることによる違いとはなんだろうか? その答えはエスケープシーケンス、特にバックスラシュの振る舞いにある。 正規表現を通して文字列中にあるバックスラッシュを得るには、 二つのバックスラッシュを置かなければならない。

例えば、/\*/`*'という文字にマッチする正規表現定数であり、 これはバックスラッシュ一つを必要とする。 同じものを文字列を使って行うには、"\\*"としなければならないだろう。 最初のバックスラッシュは二個めのバックスラッシュをエスケープするものであり、 これによってこの文字列は`\'`*'という二つのキャラクタからなる文字列となる。

正規表現を記述するのに正規表現定数と文字列定数の両方が使えるとき、 どちらを使ったほうが良いのだろうか? その答えは"正規表現定数"である。 以下にその理由を挙げる。

Advanced Notes: 動的正規表現のキャラクタリストで \n を使う

一部の商用 awk では 動的正規表現のキャラクタリストの中で 改行を使うことを許していない:

 
$ awk '$0 ~ "[ \t\n]"'
error--> awk: newline in character class [
error--> ]...
error-->  source line number 1
error-->  context is
error-->          >>>  <<<

しかし正規表現定数中の改行には問題はない:

 
$ awk '$0 ~ /[ \t\n]/'
here is a sample line
-| here is a sample line
Ctrl
-d

gawk does not have this problem, and it isn't likely to occur often in practice, but it's worth noting for future reference.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2.9 Where You Are Makes A Difference

[訳注] Windows版では関係ありません。

現代的なシステムでは、ロカール(locales)をサポートしている: システムに対してその地域のキャラクタセットや言語を知らせるものである。 カレントのロカール設定は正規表現マッチングの動作に影響を及ぼし、 しばしば驚きの元となる。特に、多くのロカールでは あなたが大小文字いずれかのキャラクタを特定していたとしても 大小文字の違いを無視したマッチングを行う。

以下に挙げた例はsub関数を使ったもので、テキストの 置き換えを行うものである(see section 文字列操作関数)。 ここで、行末にある大文字のキャラクタ列を取り除こうとしている:

 
$ echo something1234abc | gawk '{ sub("[A-Z]*$", ""); print }'
-| something1234

`something1234abc'の末尾にある`abc'は通常は`[A-Z]*'に マッチすべきでないのでこの結果は予期しないものである。 これはロカール設定によるものである(したがってあなたの使っている システムによってはこのような結果にはならないだろう)。 対応策は二つある。一つ目はPOSIXのキャラクタクラス`[[:upper:]]'`[A-Z]'の代わりに使うというものである。 (これが好まれるのは、あなたのプログラムはどこでも使えるようになるからである。) 二つ目は、ロカール設定をgawkを実行する前に変更してしまう というものである:

 
LANG=C LC_ALL=C
export LANG LC_ALL

`C'という設定はgawkに伝統的なUnixでのやり方、 つまり大小文字を区別するという動作を強制する。 あなたはこれらの文をシェルのスタートアップファイル(`$HOME/.profile'など) に追加しようと考えるかもしれない。

同様なことが別の範囲についても存在する。たとえば、 `["-/]'はASCIIにおいては完全に合法なものであるが、 `en_US.UTF-8'のような多くのUnicodeロカールにおいては 正当なものではない (一般的には、このような範囲は使うべきではない。代わりに ここのキャラクタを並べたリストか、`[[:punct:]]'のような POSIXのキャラクタクラスを使用する)。

`RS = "\n"'は通常の場合、ロカールは無関係なものである。 他の単一文字のレコードセパレータは、`LC_ALL=C' を用いることでレコードを 読む際により良いパフォーマンスをもたらすであろう。 そうでなければ、gawk入力されるキャラクタごとに レコード 区切りを見つけるためにいくつかの関数呼び出しを行わなければならない。

最後に、ロカールは gawk が入力データを解析する際に用いる小数点の 文字の値にも影響を与える。これは 変数 にて詳説する。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3. 入力ファイルを読む

典型的なawkプログラムでは、すべての入力は標準入力 (デフォルトではキーボードだが、ほとんどの場合は他のコマンドからのパイプ)か コマンドラインで指定した名前を持つファイルのどちらかから読み込まれる。 入力ファイルを指定している場合、 awkは指定した順番に従ってすべてのファイルからデータの読み込みを行う。 ある時点で処理している入力ファイルのファイル名は 組込み変数FILENAME(see section 組み込み変数)から得ることができる。

入力はレコードと呼ばれる単位で読み込まれ、プログラムに記述されたルールに従い、 一度に一つのレコードを処理する。デフォルトでは各レコードはひとつの行である。 各レコードはフィールドと呼ばれる塊に自動的に分割される。 これはあるレコードの一部分について作業を行うプログラムに便利である。

まれに特別な場合に、getlineコマンドを使う必要にせまられることがあるだろう。 getlineコマンドは任意の数のファイルから陽に入力を行うことを可能にし、 コマンドライン上で名前を指定しなくてもファイルから読み込みができるので重宝する (see section getlineを使った入力)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.1 入力がどのようにレコードに分割されるか

awkユーティリティはawkプログラムに対する入力を レコードとフィールドに分割する。 awkは現在処理している入力ファイルから読み出した レコードの数を記録している。この値は組み込み変数FNRに格納されている。 これは新たなファイルを処理し始めたときにはゼロにリセットされる。 別の組み込み変数にNRがある。これはすべてのデータファイル から 読み出した入力レコードの数を格納している。変数の値はゼロから始まるが リセットされることはない。

レコードはレコードセパレータと呼ばれるキャラクタによって分割される。 デフォルトでは、レコードセパレータは改行キャラクタである。 これはデフォルトではレコードが単一の行であるからである。 組込み変数のRSに値をセットすることによって、 レコードセパレータを別のキャラクタにすることができる。

他の変数と同じように、RSawkプログラムの中で 代入演算子`='(see section 代入式)を使うことによってその値を 変更することができる。 新たなレコードセパレータのキャラクタは文字列定数を表すクォーテーションマークで 囲まれているべきである。代入を行う正しいタイミングは ほとんどの場合実行の最初、何らかの入力が行われる前である。 これにより一番最初のレコードが正しい区切りによって読み込まれる。 これを行うためには、特殊なBEGINパターンを使用する (see section 特殊パターン BEGINEND)。 例えば:

 
awk 'BEGIN { RS = "/" }
     { print $0 }' BBS-list

この例では、入力を行う前にRSに値を"/"に変更している。 これはスラッシュが先頭にある文字列であり、結果としてレコードはスラッシュで分割される。 その後で入力ファイルが読み込まれ、 awkプログラムの二番目のルール(パターンのないアクション) によって各レコードが出力される。各print文はその出力の最後に改行を追加するので、 このawkプログラムは入力のスラッシュを改行に変換してコピーする。 次にこのプログラムを `BBS-list'に対して実行したときの結果を示す:

 
$ awk 'BEGIN { RS = "/" }
>      { print $0 }' BBS-list
-| aardvark     555-5553     1200
-| 300          B
-| alpo-net     555-3412     2400
-| 1200
-| 300     A
-| barfly       555-7685     1200
-| 300          A
-| bites        555-1675     2400
-| 1200
-| 300     A
-| camelot      555-0542     300               C
-| core         555-2912     1200
-| 300          C
-| fooey        555-1234     2400
-| 1200
-| 300     B
-| foot         555-6699     1200
-| 300          B
-| macfoo       555-6480     1200
-| 300          A
-| sdace        555-3430     2400
-| 1200
-| 300     A
-| sabafoo      555-2127     1200
-| 300          C
-|

the line looks like this: `camelot' BBS のエントリが分割されていないことに注意。 元のデータファイル (see section 例のための データファイル)では、 このような行だった。

 
camelot      555-0542     300               C

他の二つ以上のボーレートを持つところとは異なり そこにはボーレートが一つだけしかなく、スラッシュがレコード中にない。 事実、このレコードは`core' BBSのためのレコードの一部として 扱われている。出力の中で区切っている改行は元々データファイル の中にあった 改行で、awkがレコードを出力するときに付加したものではない!

レコードセパレータを変更するもう一つのやり方は、 コマンドライン上で変数代入の機能を使うことである (see section その他のコマンドライン引数):

 
awk '{ print $0 }' RS="/" BBS-list

これは`BBS-list'を処理する前にRS`/'をセットする。

`/'のような一般的でないキャラクタをレコードセパレータに使うことは、 ほとんどすべての場合に正しい結果を生じる。 しかしながら、以下の様な(極端な)パイプラインでは、意外にも`1'が出力される。 そこには改行からなる一つのフィールドがある。

 
$ echo | awk 'BEGIN { RS = "a" } ; { print NF }'
-| 1

ここには改行からなる一つのフィールドがある。 組込み変数 NF の値はカレントレコードのフィールドの数である。

入力ファイルの終端に達すると、そのファイルの最後のキャラクタが RSにあるキャラクタでなくてもカレント入力レコードを終端する (d.c.) 。

空文字列""(キャラクタが何もない文字列)は、 RSの値としては特別な意味を持っている。 それは、レコードを一行以上の空行で区切るということである。 詳しくはSee section 複数行レコード.。

awkの実行中にRSの値を変更した場合、 その新しい値は後続のレコードの区切りに使用されるが、 現在処理されているレコード(と既に処理されたレコード) にはなんの影響も及ぼさない。

レコードの区切りが終了した後で、gawkRSにマッチした入力中の テキストを RT という変数にセットする。 gawkを使っているときにはRSの値は一文字に限定されるものではなく、 任意の正規表現 (see section 正規表現)を使用することができる。 一般的には各レコードの終端にはその正規表現にマッチした文字列が続いており、 次のレコードはマッチした文字列の終端の次から始まる。 この一般的なルールは通常のRSが改行だけからなっているようなときにはうまく働く。 レコードは次のマッチする文字列の先頭(入力の次の改行)で終わり、 続くレコードはこの文字列のすぐ後(続く行の先頭のキャラクタ)から始まる。 改行はRS にマッチするので、いずれのレコードの一部分にもならない。

RSが単一のキャラクタであったとき、 RTはそれと同じキャラクタ一文字になる。 しかし、RSが正規表現であるとき、RTはもっと便利になる。 つまり、実際に正規表現にマッチした入力テキストが格納されるのである。

次に挙げる例はこれらの機能を両方とも説明している。RSは改行にも、 (前後に連続した空白が付いてもよい)一つ以上の連続した大文字にもマッチする 正規表現がセットされている:

 
$ echo record 1 AAAA record 2 BBBB record 3 |
> gawk 'BEGIN { RS = "\n|( *[[:upper:]]+ *)" }
>             { print "Record =", $0, "and RT =", RT }'
-| Record = record 1 and RT =  AAAA
-| Record = record 2 and RT =  BBBB
-| Record = record 3 and RT =
-|

of RS as a regexp and RT. 出力の最後の行は余計な空白行がある。これは、RTの値が改行で、 さらにprint文が改行を最後につけているからである。 正規表現としてのRSRTの便利な例は See section 単純なストリームエディタ.

RSに、`RS = "abc(XYZ)?"'のような省略可能な 末尾を持つ正規表現をセットした場合、これは可能であるけれども 実装上の制限により、gawkは正規表現の先行する部分に マッチして末尾部分を無視してしまう。特に入力テキストが 長い場合。gawkはこのような問題を避けようと努力するけれども、 絶対にこのような事態にならないとはいえないのである。

NOTE : awkでは、`^'`$'といったアンカーメタキャラクターは それぞれ文字列の始端と終端にマッチし、の 始端と終端にマッチするわけではない。結果として、`RS = "^[[:upper:]]"' のような正規表現はファイルの先頭にのみマッチする。 これはgawkが入力ファイルを一つの長い文字列としてみているためで 単にその中に改行が含まれているというだけのことである。 したがってRSの値としてアンカーキャラクターを使わないことが 一番である。

RSを正規表現として使ったりすることや、RT変数は gawkの拡張である。これらは互換モード(see section コマンドラインオプション)で 使うことはできない。 互換モードでは、RSの先頭のキャラクターだけが レコードの終端を決めるのに使われる。

Advanced Notes: RS = "\0" Is Not Portable

データファイル 全体を一つのレコードとして扱いたいときがあるかもしれない。 これを行うただ一つの方法は、RSに入力ファイルの中に決して現れない 値を設定するというものである。これは 任意の入力ファイルに対して常に動作するプログラムのような 一般的なものには難しい方法である。

あなたはこの場合に、テキストファイルに対しては、全てのビットがゼロのキャラクターである NULキャラクターをRSに対して使うことが良いと考えるかもしれない:

 
BEGIN { RS = "\0" }  # ファイル全体が一つのレコードになる?

gawkは実際にこれを受け付け、NULキャラクターを レコードセパレーターとして使うことができる。しかしながら、 このような使い方は他の実装のawkに対してポータブルなものではない

他の全てのawkの実装(16)は文字列を内部的にはC形式の文字列として格納している。 C形式の文字列はNULキャラクターを文字列の終端として使用している。 このため、`RS = "\0"'`RS = ""'と等価となる。 (d.c.)

ファイル全体を一つのレコードとして扱う最も良いやり方は、 単純にファイルを一度に一レコード読み、各レコードを ひとつ前の終端につなげてしまうというものである。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.2 フィールドを検査する

awkが入力レコードを読み込んだとき、 そのレコードは自動的にインタープリタによって解析され、 フィールドと呼ばれる塊に分割される。 デフォルトではフィールドの分割は行の中にある単語のように、 空白(whitespace)によって行われる。 awkでの空白は、スペースかタブ、あるいは改行の一つ以上の連なりである (17)。 ほかの言語では空白とみなされるようなフォームフィードや垂直タブなどのキャラクタは、 awkの場合は空白としてはみなされない

フィールドの目的は、レコードのこういった塊を参照するのに便利なようにするためであり、 フィールドを使わなければならないというものではない。 望むならレコード全体を処理することもできる。 しかし、フィールドはパワフルなawkプログラムを簡単にするのである。

awkプログラム中でフィールドを参照するには、ドル記号(`$')に 続けて参照したいフィールドの番号を続けることによっておこなう。 したがって、$1は最初のフィールドを、$2は二番目のフィールドを参照する (Unixのシェルとは異なり、フィールド番号は一桁に限らない。$127は レコードの127番目のフィールドである)。 例えば次のような行が入力されたとすると、

 
This seems like a pretty nice example.

この例の場合、最初のフィールドである$1の内容は`This'である。 二番目のフィールド、$2の内容は`seems'というようになる。 最後のフィールド$7`example.'であることに注意して欲しい。 これは、`e'`.' の間にスペースがないためで、 ピリオドは七番目のフィールドの一部であるとみなされているのである。

NFはカレントレコードにあるフィールドの数を保持する組み込み変数である。 awkはレコードの読み込みのたびにNFの値を自動的に更新する。 フィールドが幾つあるかにかかわらず、 あるレコードの最後のフィールドは $NFで表わすことができる。 だから先程の例でいうと、$NF$7と同じことであり、 内容は `example.' になる。 レコード中にフィールドが七個しかないときに $8のように最後の要素を越えて参照しようとしたときには、空の文字列が返ってくる (数値オペレーションで使用した場合にはゼロが得られる)。

$0は"0番目"のフィールドを参照するように見えるが、 これは特別なケースで入力レコード全体を表わす。 $0はフィールドを気にしたくないときに使用する。 さらに幾つかの例を挙げよう:

 
$ awk '$1 ~ /foo/ { print $0 }' BBS-list
-| fooey        555-1234     2400/1200/300     B
-| foot         555-6699     1200/300          B
-| macfoo       555-6480     1200/300          A
-| sabafoo      555-2127     1200/300          C

この例では、`BBS-list' というファイル中のレコードで、 レコードの第一フィールドに `foo'という文字列を含んでいるものを出力している。 `~'という演算子はマッチング演算子(matching operator) (see section 正規表現の使い方)と呼ばれ、 ある文字列(ここではフィールド$1)と与えられた正規表現とがマッチするかを検査する。

一方次の例は、レコード全体から`foo'を探して、 それが見つかった入力レコードの第一フィールドと最終フィールドを出力する:

 
$ awk '/foo/ { print $1, $NF }' BBS-list
-| fooey B
-| foot B
-| macfoo A
-| sabafoo C

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.3 定数でないフィールド番号

フィールドの番号は定数である必要はない。あるフィールドを参照するために、 awk言語での任意の式を`$'の後で使うことができる。 その式の値はフィールド番号として扱われる。 このとき式が数値ではなく文字列であった場合、 その式の値は数値へと変換される。例を挙げよう:

 
awk '{ print $NR }'

NRはそれまでに読み込んだレコードの数だということを思い出そう。 一番目のレコードでは1、二番目では2…というようになる。 したがってこの例は、一番目のレコードの一番目のフィールド、 二番目のレコードの二番目のフィール…と出力していく。 二十番目のレコードでは二十番目のフィールドが出力されることになるが、 ほとんどの場合はレコードには20未満のフィールドしかないから、空白行が出力されるだろう。 もう一つフィールド番号として式を使った例を挙げる:

 
awk '{ print $(2*2) }' BBS-list

awk`(2*2)'という式を評価し、 それからその値を出力すべきフィールドの番号として使用する。 `*'は乗算を表すので、`2*2' という式を評価すると4となる。 括弧は`$'のオペレーションより先に掛け算を行わせるためである。 フィールド番号を指定する式で二項演算子を使うときは常にこのようにする必要がある。 この例では`BBS-list'というファイル中の全ての行の、 運営時間(4番目のフィールド)を出力する (awkで使える演算子の全ては 演算子の優先順位 (How Operators Nest) に一覧がある)。

フィールド番号が0になるような式を与えると、その結果はレコード全体となる。 だから、 `$(2-2)'の値は$0と同じである。 負の数によるフィールド番号の指定は許されおらず、 もしそういったフィールドを参照しようとすると、 通常はawkプログラムの実行が中断される (POSIXの標準では、負のフィールド番号を参照したときの動作は定義されていない。 gawkではこれを通知して、プログラムを終了する。 他のawk処理系では違った動作をするかもしれない)。

フィールドを検査するで述べられているように、 カレントレコードのフィールド数は組込み変数の NF (see section 組み込み変数)に格納されている。 $NFという式は特別な機能ではなく、NFを評価して、 その値をフィールド番号として使った結果である。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.4 フィールドの内容を変更する

awkプログラムの中でフィールドの構成を変更することができ、 それによってawkはカレント入力レコードが変更されたと認識する (実際の入力には影響ない。awk決して入力ファイルを変更しない)。 以下のプログラムの出力を考えてみよう:

 
$ awk '{ nboxes = $3 ; $3 = $3 - 10
>        print nboxes, $3 }' inventory-shipped
-| 25 15
-| 32 22
-| 24 14
…

このプログラムは最初に変数nboxesに三番目のフィールドの元の値を 保存している。`-'記号は減算の記号であるので、 このプログラムは三番目のフィールド$3`$2 - 10'、 つまり二番目のフィールドの値から10を引いた値をセットする (See section 数学的演算子.)。 それから二番目のフィールドと新しい三番目のフィールドが出力される (Someone in the warehouse made a consistent mistake while inventorying the red boxes.)。

これがきちんと動作するには、 フィールド$2のテキストは数として意味のあるものでなければならない。 (フィールドの)文字列は算術演算を行うために数値に変換される。 引き算の結果(の数値)は再度文字列に変換されて、第三フィールドになる。 See section 文字列と数値の変換.

あるフィールドの値を変更した(awkがそう認識した)とき、 入力レコードのテキストは新しいフィールドが古いフィールドのところに あるように再演算が行われる。そのため、 $0がフィールドの変更を反映して変化するのである。 したがって、次のプログラムは、入力ファイルのデータの各行の第二フィールドが 入力ファイルにあったものから10を引いたものに置き換えられた 結果を出力するのである:

 
$ awk '{ $2 = $2 - 10; print $0 }' inventory-shipped
-| Jan 3 25 15 115
-| Feb 5 32 24 226
-| Mar 5 24 34 228
…

最大のフィールド番号より大きい、存在しないフィールドに対する代入も可能である。 例を挙げよう

 
$ awk '{ $6 = ($5 + $4 + $3 + $2)
>        print $6 }' inventory-shipped
-| 168
-| 297
-| 301
…

ここでは$6が新たに作り出されて、 $2, $3, $4, $5の各フィールドの合計が格納される。 `+'は足し算の記号である。 `inventory-shipped'というファイルを使って各月ごとの積込量の合計を $6に設定する。

新しいフィールドを作成すると、awk内部にある カレントレコードのコピーが変更される。したがって、 フィールドを付け加えた後で`print $0'を出力すると その結果には 以前から存在していたフィールドと新たなフィールドとの間に 適切な数のフィールドセパレータと、 新しいフィールドが含まれている。

この再計算の効果と再計算を行う機能についての説明はまだされていない。 フィールドの区切りを指定する出力フィールド指定子OFSや NF (フィールドの数。see section フィールドを検査する)についての説明もまだである。 例えば、NFの値は新たに作り出されたフィールドの フィールド番号の最大のものがセットされる。 $0の正確なフォーマットもまたまだ説明していない機能 - 出力ファイルセパレータ(OFS)が フィールドを区切るものとして使われる(see section 出力セパレータ) - に影響を受けている。

しかし気をつけなければならないのは、範囲外のフィールドに対する参照$0の値もNFの値も変更しないということである。 範囲外のフィールドに対する参照は単に空文字列を生成するだけである。例を挙げよう:

 
if ($(NF+1) != "")
    print "can't happen"
else
    print "everything is normal"

これは`everything is normal'が出力されるはずだ。 なぜなら、NF+1は範囲の外にあるからである (awkif-else文についての詳しい情報は See section if-else`!='演算子に関する詳しい情報は See section 変数の型付けと比較式.)。

既に存在するフィールドに何かを代入することによって $0の値は変化するがたとえフィールドに空文字列を代入したとしても NFの値は変化しないということは重要である。例えば:

 
$ echo a b c d | awk '{ OFS = ":"; $2 = ""
>                       print $0; print NF }'
-| a::c:d
-| 4

フィールドは空の値を持って存在しつづけている。 それは`a'`c'の間に二つのコロンがあることで 示されている。以下の例は新たなフィールドを生成したときに 起こることを説明している:

 
$ echo a b c d | awk '{ OFS = ":"; $2 = ""; $6 = "new"
>                       print $0; print NF }'
-| a::c:d::new
-| 6

$5が間に入って空の値で作成されていて (二つめの並んだコロンによって判断できる)、 NFの値が6に更新されている。

NFの値を減じることによって新たなNFの値より 後ろのフィールドの値は失われることとなり、$0の再計算が行なわれる。 (d.c.) 以下はその例である:

 
$ echo a b c d e f | awk '{ print "NF =", NF;
>                            NF = 3; print $0 }'
-| NF = 6
-| a b c

警告: 一部のバージョンのawkNFを減じたときに$0の再構築を行わない。 Caveat emptor。

結局のところ、awkにレコード全体の再構築を強制したときには その時点でのフィールドの値とOFSが使われる。 再構築を行うためには、何らかの無害な代入を行う:

 
$1 = $1   # force record to be reconstituted
print $0  # or whatever else with $0

これはawkにレコードの再構築を強制する。 上記の例のように、コメントを付加して理解の助けにしている。

$0とフィールドとの間には関係がある。$0に対する代入は FSその時点での値を使った レコードからフィールドへの再構築を引き起こす。 これはまたsubgsub(see section 文字列操作関数)のような 任意の組み込み関数の適用による$0の更新にも適用される。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.5 フィールドがどのように分割されるかを特定する

The field separator, which is either a single character or a regular expression, controls the way awk splits an input record into fields. awk scans the input record for character sequences that match the separator; the fields themselves are the text between the matches. フィールドセパレータはある一文字か、あるいは正規表現であり、 awkが入力レコードをどのようにフィールドに分割するかを制御する。 awkは入力レコード中にそのセパレータにマッチするキャラクタの 並びがないか検索する。フィールドそのものはマッチしたものの間にあるテキストである

次に挙げる例では、出力中の空白を表すのにbullet symbol (•)を 使っている。フィールドセパレータが`oo'であるとき以下の 行が与えられたとすると:

 
moo goo gai pan

三つのフィールドに分割される: `m', `•g', `•gai•pan'。 二番目と三番目のフィールドの先頭に空白があることに注意すること。

フィールドセパレータは組込み変数のFSで示される。 シェルプログラマは注意すること! awkは (Unix Bourne シェル、shbashのような) POSIX互換のシェルが使っているIFSという名前の変数は使わない

awkプログラム中で代入演算子`=' (see section 代入式) を使ってFSの値を変更することができる。 ほとんどの場合、これ(FSの値を変更すること)を行うのに正しいタイミングは 実行の開始時、入力が処理される以前である。 これは一番最初のレコードが適切なセパレータで読み込みが行われるからである。 このタイミングで変更を行うためにはBEGINスペシャルパターンを使う (see section 特殊パターン BEGINEND)。 例えば、次の例ではFSの値を","という文字列に設定している。

 
awk 'BEGIN { FS = "," } ; { print $2 }'

入力として次のものを与える:

 
John Q. Smith, 29 Oak St., Walamazoo, MI 42139

先のawkプログラムは `•29•Oak•St.' という文字列をレコードから取り出し、出力する。

ときとして、セパレートキャラクタを含んではいるがそれを分割したくはない というデータを扱いたいときがあるかもしれない。 例えば、先の例の中にある人名は以下のように タイトルやサフィックスがついている可能性がある:

 
John Q. Smith, LXIX, 29 Oak St., Walamazoo, MI 42139

先のサンプルプログラムは`•29•Oak•St.'ではなく、 `•LXIX'を取り出す。 もしプログラムが住所を出力すると期待していたならば、その結果に驚かされるかもしれない。 だから、この様な問題を避けるためにデータのレイアウトと、 セパレータとなるキャラクタは慎重に選択せねばならない (もしデータが簡単に処理できるような書式でないのなら、まず最初に 別のawkプログラムで整形することができるだろう)。

FS is a special case--it is taken to specify the default manner 通常、フィールドは一つの空白ではなく空白の並び(スペースかタブか改行)で区切られる。 行にある二つのスペースは空のフィールドを区切らない。 フィールドセパレータのデフォルトの値は " "という文字列であるが、 もしこの値を普通通り使ったとしたら、二つの空白からなるような行は空白は 二つのフィールドセパレータとして扱われ、結果として awkは空のフィールドを作りだすのだろうか? 実際には以下の理由によってこのようなことは行われない。 FSの値が一つの空白であった場合には特殊な場合として、 フィールドの分割のルールをデフォルトと同じ様にする

FSの値が他のキャラクタが一文字、例えば","の様な場合には そのキャラクタは二つのフィールドを分割する様な結果となる。 二つの連続したキャラクタは空のフィールドを分割する。 もし、行の先頭や末尾に(区切りの)キャラクタがあった場合には 空のフィールドがあるように区切る。 スペースはこのルールに従わないただ一つのキャラクタである。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.5.1 フィールド分割のために正規表現を使う

先のサブセクション では単一のキャラクタや単純な文字列を FSの値として用いることについて述べた。 より一般的には、FSの値は正規表現を構成する文字列にすることができる。 この場合、レコードの中でそれにマッチするものがフィールドを分割する。 たとえば:

 
FS = ", \t"

このような代入は、 カンマ、スペース、タブが後ろに続いている領域がフィールドになる 。

正規表現のあまり重要でない例として、先の例のカンマのように、 一つのスペースでフィールド分割を行いたいという状況を考えよう。 これは、FS"[ ]" (左ブラケットスペース、右ブラケット) をセットすることでできる。この正規表現は一つのスペースにマッチし、 他のものにはマッチしない(see section 正規表現)。

`FS = " "'(一つのスペース)と、 `FS = "[ \t\n]+"' (一つ以上のスペース、タブにマッチする正規表現)の間の違いは重要である。 これら両方のFSの値は、共にフィールド分割をスペース、タブ、改行(の連続)で行う。 しかし、FS" "であったとき、awkは最初に、 先頭や末尾で連続している空白のはぎ取りを行い、それからフィールド分割を実行する。 例を挙げると、次のパイプラインでは`b'が出力される:

 
$ echo ' a b c d ' | awk '{ print $2 }'
-| b

しかし、次のパイプラインでは`a'が出力される (それぞれの文字の回りには余計なスペースが付いていないことに注意):

 
$ echo ' a  b  c  d ' | awk 'BEGIN { FS = "[ \t\n]+" }
>                                  { print $2 }'
-| a

この場合、第一フィールドは(null、empty)である。

先頭や末尾にある空白は$0が再構成されたときにはいつでも取り除かれる。 例えば次のパイプラインで:

 
$ echo '   a b c d' | awk '{ print; $2 = $2; print }'
-|    a b c d
-| a b c d

最初のprint文は読み込んだレコードをレコードの先頭にある空白も含めて そのまま出力する。$2に対する代入は、$1から $NFまでをOFSの値で分割されたものとして連結して $0を再構成する。先頭の空白は$1に注目したときには無視されているので、 新しい$0には含まれない。そして、最後のprint文は新しい$0を出力する。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.5.2 ここのキャラクタを別個のフィールドにする

レコード中の個々のキャラクタを別個にチェックしたいと思うことはままあるだろう。 gawkでは、それを簡単に行える。 単に空文字列("")を FSにセットするに代入すればよい。 この場合、レコード中の個々の独立したキャラクタがそれぞれ一つのフィールドとなる。 例を挙げると:

 
$ echo a b | gawk 'BEGIN { FS = "" }
>                  {
>                      for (i = 1; i <= NF; i = i + 1)
>                          print "Field", i, "is", $i
>                  }'
-| Field 1 is a
-| Field 2 is
-| Field 3 is b

伝統的に、FS""にセットされているときの振る舞いは未定義だった。 この場合UNIXのawkは、単にレコード全体を一つのフィールドであるように扱う (d.c.) gawkは互換モード(see section コマンドラインオプション)では、 FSが空文字列の場合にはこのような動作をするようになる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.5.3 コマンドラインから FS を設定する

FSはコマンドライン上でセットすることができる。それには`-F'オプションを使用する。 例を挙げよう:

 
awk -F, 'program' input-files

この例ではFS`,'というキャラクタにセットしている。 このオプションが小文字の`f'ではなく大文字の`F'を使っていることに注意。 後者の`-f'というオプションは、 awkプログラムが入っているファイルを指定するものである。 コマンドラインオプションの大小文字は区別されるので、 `-F'`-f'という二つのオプションはまったく別のものである。 これら両方のオプションを同時に使って、 変数FSに(セパレータを)セットするのと同時に awkプログラムをファイルから得ることができる。

`-F'の引数として使った値は、組込み変数FS に代入したときとまったく同じように扱われる。 どういうことかというと、もしフィールドセパレータがスペシャルキャラクタを含んでいたら、 それを適切にエスケープしなければならないということである。 例えば、`\'をフィールドセパレータとして使おうとするなら、 次のようにタイプする必要がある:

 
# same as FS = "\\"
awk -F\\\\ '…' files …

`\'はシェルによってクォーティングされるので、awkには `-F\\'が実際には渡される。awkはそれからエスケープシーケンス の処理を`\\'に対して行うので、最終的にはただ一つの`\'が フィールドセパレータとして使われることになる。

特殊なケースとして、互換モード(see section コマンドラインオプション)において `-F'の引数が`t'であったとき、FSにはタブキャラクタが セットされる。シェルにおいて`-F\t'とクォートなしに入力したとき、 `\'は削除されるのでawkはあなたは実際には フィールドが`t'ではなくてタブで区切られているのだろうと 判断するのである。本当に`t'でフィールドを区切りたいのなら コマンドライン上で`-v FS="t"'`-F"[t]"'とする。

たとえば、`baud.awk'という名前の/300/というパターンと `print $1'というアクションからなるプログラムファイルを使ってみよう:

 
/300/   { print $1 }

`BBS-list'に対して実行するときに`-'FSにセットした とする。以下はコマンドが出力した300ボーで運営されている BBSの名前とその電話番号の最初の三桁のリストである:

 
$ awk -F- -f baud.awk BBS-list
-| aardvark     555
-| alpo
-| barfly       555
-| bites        555
-| camelot      555
-| core         555
-| fooey        555
-| foot         555
-| macfoo       555
-| sdace        555
-| sabafoo      555

出力の二行目に注目してほしい。オリジナルのファイルの 二行目は以下のようになっていた:

 
alpo-net     555-3412     2400/1200/300     A

システムの名前の一部にある`-'が電話番号の`-'の代わりに フィールドセパレータとして見なされてしまっている。 これはあなたがフィールドセパレータやレコードセパレータを選択するときには 注意深くあらねばならないということの実例である。

単一のキャラクタをフィールドセパレータとする最も一般的なのは 多分Unixシステムのパスワードファイルを処理するときだろう。 多くのUNIXシステムでは、ユーザーはシステムのパスワードファイル中に ユーザー一人当り一行のエントリをそれぞれ所有している。 そこにある情報はコロン(`:')によって区切られていて、 最初のフィールドにはユーザーのログオン名が、 二番目のフィールドにはそのユーザーの暗号化されたパスワードがある。 パスワードファイルのエントリはおおむね次のようなものである。

 
arnold:xyzzy:2076:10:Arnold Robbins:/home/arnold:/bin/bash

次のプログラムはシステムのパスワードファイルを検索して、 パスワードが設定されていないエントリを出力する:

 
awk -F: '$2 == ""' /etc/passwd

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.5.4 フィールド分割のまとめ

FSの値として文字列定数を代入するときに心に留めておくべき 重要なことは、通常のawkの文字列処理が行われるということである。 たとえばUnixのawkgawkでは、 `FS = "\.."'という代入はFS".."という文字列を 代入する(バックスラッシュが取り除かれる)。これは “フィールドは任意のキャラクタ二つの並びによって分割される”という 正規表現を生成する。もしフィールドを任意のキャラクタが後続する ピリオドによって分割したいのなら、`FS = "\\.."'を使用する。

次のテーブルはFSの値に基づいてどのようにフィールド分割が行われるか まとめたものである(`=='は“に等しい”を意味する):

FS == " "

フィールドは空白の並びによって分割される。 先行する空白や末尾の空白は無視される。これはデフォルトである。

FS == 他の任意の単一キャラクタ

フィールドはそのキャラクタによって分割される。 キャラクタの並びは空白のフィールドを生成し、先行するものや 末尾のものも処理される。キャラクタは正規表現のメタキャラクタで あってもよい。これはエスケープする必要はない。

FS == 正規表現

フィールドは正規表現にマッチした(単一、もしくは複数の)キャラクタによって 分割される。正規表現にマッチした先行するものや末尾のものは空のフィールドを 分割する。

FS == ""

レコード中の各キャラクタが分割されたフィールドとなる (これはgawkの拡張である。POSXI標準では規定されていない)。

Advanced Notes: FS の変更はフィールドに影響しない

POSIXの標準に従えば、awkはレコードを読み込んだときに フィールドの分割をするように動作することになっている。 これはレコードの読み込みが終わった後でFSの値を変更できるということであり、 フィールドの値(どのように分割されたか、ということ)は新しいものではなく、 古い変更前のFS の値を反映したものであるべきであるということである。

しかし、多くのawk処理系でこの動作をしておらず、 実際にフィールドが参照されるまで分割を遅らせている。 この場合、フィールドは新しいFSの値を使って分割されるのだ!(d.c.) この振る舞いは調べるのが難しい。 以下の例はこれら二つのやり方の違いを説明するものである (このsed(18) コマンドは`/etc/passwd'の最初の一行だけを出力する)。

 
sed 1q /etc/passwd | awk '{ FS = ":" ; print $1 }'

これは正しくない実装のawkでは以下の出力を行う:

 
root

一方gawkでは以下のような出力となる:

 
root:nSijPlPhZZwgE:0:0:Root:/:

Advanced Notes: FSIGNORECASE

IGNORECASE変数(see section awkを制御する組み込み変数)は FSが正規表現であるときにのみフィールド分割に影響を及ぼす。 FSが単一キャラクタであるときにはたとえそれが通常の文字であったとしても 影響しない。したがって:

 
FS = "c"
IGNORECASE = 1
$0 = "aCa"
print $1

この出力は`aCa'となる。本当に大小文字の区別を無視してアルファベット でフィールド分割を行いたいのなら、それを表す正規表現、 たとえば`FS = "[c]"'を使用する。この場合にはIGNORECASEは 影響を及ぼすことになる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.6 固定長データを読む

注意 : このセクション セクションでは、先進的なgawkの機能について説明している。 もしあなたがawk初心者なら、 初めて読んだときにはここを飛ばしたくなるかもしれない。

gawk 2.13 で新たに、幅が固定されていてセパレータを持っていない フィールドを扱うための機能を導入した。 そこで想定されているデータは典型的には古いFORTRANプログラムに対する数値の入力として、 あるいは他のプログラムの入力として扱われることを考慮していないプログラムの 出力の二つのうちのいずれかだろう。

後で挙げる例は、全てのカラムが可変数のスペースを使用して整列されていて、 空のフィールドがスペースで構成されているデータのテーブルである。 現実に、 awkの通常のFSに基づいて行うフィールド分割は この様なケースではうまく働かない。 awkプログラム中で$0に対してsubstrをくり返し呼ぶことで 実現できるが (see section 文字列操作関数)、これはポータブルであっても、 awkward であり、大きなフィールド番号では非効率的なやり方である。

入力レコードを固定幅のフィールドに分割するときには、 組み込み変数FIELDWIDTHS に設定されたスペースで区切られた数字の並びで指定する。 並びのそれぞれの数字は、フィールドの幅をフィールドの間のカラムを含めて指定している。 もし、フィールドの間のカラムを無視したいのであれば、 後続を無視するようなフィールドの分割をする幅を指定することもできる。 正でない数をフィールド幅として与えると致命的エラーとなる。 次のデータはwユーティリティの出力である。 これはまたFIELDWIDTHS の使い方を説明するのにちょうどよい。

 
 10:06pm  up 21 days, 14:04,  23 users
User     tty       login  idle   JCPU   PCPU  what
hzuo     ttyV0     8:58pm            9      5  vi p24.tex
hzang    ttyV3     6:37pm    50                -csh
eklye    ttyV5     9:53pm            7      1  em thes.tex
dportein ttyV6     8:17pm  1:47                -csh
gierd    ttyD3    10:00pm     1                elm
dave     ttyD4     9:47pm            4      4  w
brent    ttyp0    26Jun91  4:46  26:46   4:41  bash
dave     ttyq4    26Jun9115days     46     46  wnewmail

次のプログラムは先ほどのデータを入力とし、 アイドルタイムを秒に変換して入力データの最初の二つのフィールドと、計算した時間を出力する:

注意 : このプログラムはまだ説明されていないawkの機能をいくつか使っている。

 
BEGIN  { FIELDWIDTHS = "9 6 10 6 7 7 35" }
NR > 2 {
    idle = $4
    sub(/^  */, "", idle)   # 先行する空白を取り除く
    if (idle == "")
        idle = 0
    if (idle ~ /:/) {
        split(idle, t, ":")
        idle = t[1] * 60 + t[2]
    }
    if (idle ~ /days/)
        idle *= 24 * 60 * 60

    print $1, $2, idle
}

プログラムの実行結果は次の通り:

 
hzuo      ttyV0  0
hzang     ttyV3  50
eklye     ttyV5  0
dportein  ttyV6  107
gierd     ttyD3  1
dave      ttyD4  0
brent     ttyp0  286
dave      ttyq4  1296000

別の(より現実的であろう)固定幅の例となる入力データは、投票用紙の山からの入力だろう。 合衆国の一部では、投票者は投票をコンピュータカードのパンチされた穴を選択することで行う。 これらのカードは投票がどの候補者に行われたか、 あるいはどの欄が選択されたかを数えるのに使われる。 投票者が一部の投票を行わなかった場合、対応するカードのカラムは空である。 awkプログラムはその様なデータを処理するために、 FIELDWIDTHS 機能を使ってデータを単純に読むことができる (もちろん、カードリーダー付きのシステムでgawkを動かすようにするのは別の話だ!)。

FSへの代入は、再度FSによるフィールド分割をgawkに 指示する。`FS = FS'はこれを現在のFSの値を知ることなしに 行うことができる。フィールド分割がどの種のもので行われているかは PROCINFO["FS"]を使うことによって知ることができる (see section 情報を伝達する組み込み変数)。 通常のフィールド分割が行われていればその値は"FS"であり、 固定長のフィールド分割が行われていれば"FIELDWIDTHS"が値となる:

 
if (PROCINFO["FS"] == "FS")
    通常のフィールド分割 …
else
    固定長フィールド分割

この情報は、幾つかのレコードを読み取り、その後で元の設定に戻すような FSFIELDWIDTHSを一時的に変更する必要がある 関数を記述するときに便利である(そのような関数の例は see section Reading the User Database にある)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.7 複数行レコード

一部のデータベースでは、一つの情報のエントリを一行で保持できない。 この様な場合に複数行レコードを使うことができる。 これを行う最初のステップはデータフォーマットの選択である。

一つの手段としては、レコードを分割するのにあまり使われないようなキャラクタか 文字列を使う。ということがある。 例えばフォームフィードのコード (awkではCのように、`\f'と記述する) をファイルをページを一つのレコードとして分割するために使うことができるだろう。 そうするには変数RS"\f" (フォームフィードキャラクタを保持する文字列)をセットすればよい。 他の、レコード中のデータの一部分として同じ程度に あまり使われていないキャラクタを使うこともできるだろう

他のやり方としては空行でレコードを分割するというものがある。 特別な場合として、空文字列をRSにセットすると、 それはレコードの区切りとして一行以上の空行をでレコードを分割するという事を示す。 もし、RSに空文字列をセットすると、レコードは常に最初の空行に 出会ったところで終わる。 そして、次のレコードは空行でない行がきたところでスタートする。 どれだけ空行が続いているのかは関係なく、 単に一つのレコードセパレータとして認識される (空行は完全に空でなければならない。空白のみからなる行は含まれない)。

`RS = ""' としたときと同じ効果を"\n\n+"RSに セットすることによって得ることができる。 この正規表現はレコードの終端にある改行にマッチし、 一行以上の空白行がレコードの後に続く。 それに加えて、正規表現は常にマッチする最長のパターンを選択する (see section どのくらいテキストがマッチするか?)。 したがって次のレコードは空行でない行がくるまでスタートしない。 どれだけ空行が続いてあるかは関係なく、単に一つのレコードセパレータとして認識される

(d.c.) `RS = ""'`RS = "\n\n+"'には重要な違いがある。 最初のものは、 入力データファイル中の先頭にある改行は無視され、 最後のレコードの後に空行がない場合には、最後の改行がレコードから剥ぎ取られる。 二番目のものでは、このような特殊な処理は行われない(d.c.)

ここまでで入力がレコードに分割された。 第二のステップはレコード中のフィールドをどう分割するか、という事である。 フィールドを分割する別のやり方としては、 通常の方法で各行をフィールドに分割するというものである。 これは特殊機能の結果、デフォルトで生じることである。 RS に空文字列が設定されていて、かつFSが 単一キャラクタであるとき 改行は常にフィールドセパレータとして動作する。 これは、 FSに設定されている値によるフィールド分割に加えられる形で行われる (19)

この特別な例外を取り入れることになった元々の動機は、 デフォルトの場合に使い易い動作をするようにするためである (i.e., FS == " ")。 この機能は、ユーザーが本当は改行をフィールド分割のためのキャラクタとして 使いたくないときに、それを防ぐ手段がないので問題となる。 しかしながら、 split関数を使って手動で分割を行なうことで 望む結果を得ることができる (see section 文字列操作関数)。 単一キャラクタのフィールドセパレータを使っているのなら、 FSに単一キャラクタを表す正規表現を格納するという やり方でこの特殊機能を回避することができる。 たとえばフィールドセパレータにパーセント記号を使っているのなら、 `FS = "%"'とする代わりに`FS = "[%]"'とするのである。

もう一つの方法は一行を一つのフィールドとして出力する。というものがある。 それを実行するには変数FS"\n"という文字列をセットすれば良い (これは、一つの改行にマッチする単一キャラクタによるセパレータである)。 このやり方を使った現実的な例は各エントリが空行で分割されている メイリングリストだろう。`addresses'という名前のファイルに格納されている 以下のような形態のメイリングリストを考えてみよう。

 
Jane Doe
123 Main Street
Anywhere, SE 12345-6789

John Smith
456 Tree-lined Avenue
Smallville, MW 98765-4321
…

このファイルを処理する単純なプログラムは以下のようなものである:

 
# addrs.awk --- 簡単なメイリングリストプログラム

# レコードは空行によって分割される。
# 各行は一つのフィールド。
BEGIN { RS = "" ; FS = "\n" }

{
      print "Name is:", $1
      print "Address is:", $2
      print "City and State are:", $3
      print ""
}

プログラムを実行すると以下のような結果が得られる:

 
$ awk -f addrs.awk addresses
-| Name is: Jane Doe
-| Address is: 123 Main Street
-| City and State are: Anywhere, SE 12345-6789
-|
-| Name is: John Smith
-| Address is: 456 Tree-lined Avenue
-| City and State are: Smallville, MW 98765-4321
-|
…

See section メイルのラベルを出力する, for a more realistic program that deals with address lists. The following table summarizes how records are split, based on the value of RS:

RS == "\n"

レコードは改行キャラクタ(`\n')で分割される。 言い換えると、データファイル の各行が空行も含めて、独立したレコードとなる。

RS == 任意の一キャラクタ

レコードは指定されたキャラクタにより分割される。 このキャラクタが連続している場合には、空のレコードを区切っているとみなされる。

RS == ""

空行の連続によってレコードを分割する。 FS が単一文字であった場合に、付け加えれば FS の値に 関わらず改行文字は常にフィールドセパレータとなる。 ファイルの先頭と末尾にある改行は無視される。

RS == regexp

regexpにマッチした文字列によってレコードが分割される。 先頭や末尾にあるregexpは空レコードを区切るとみなされる (これはgawkの拡張機能である。POSXI標準では規定されていない)。

すべてのケースにおいて、gawkRTRSで 指定されたものにマッチしたテキストをセットする。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.8 getlineを使った入力

これまで私達はawkの主入力ストリームから入力データを得ていた。 それは標準入力(通常はターミナル、ときとして他のプログラムの出力) やコマンドラインで指定したファイルであった。 awk言語はgetlineと呼ばれる特殊な組込みコマンドを持っており、 これは明確に制御された元での入力読み取りに使うことができる。

getlineコマンドは非常に複雑であり、初心者はあまり使うべきではない。 この章が入力に関してのものであるのでここで記述している。 以下に挙げる例はまだ説明されていないものが含まれている getlineコマンドの説明である。従って、 このWeb ページ の残りの部分を読んでawkが どのように動作するかの知識を十分得た後に、 ここに戻ってgetlineコマンドを学ぶのが良いだろう。

getlineはレコードが見付かれば1を返し、 ファイルの終端に達したときには 0を返す。 もし、レコードの読み込みでなんらかのエラーが発生したり、 ファイルがオープンできなかったりした場合にはgetlineは -1を返す。 このときgawkERRNOという変数に エラーが発生したことを表わす文字列をセットする。

次の例では、command はシェルコマンドを表す文字列である。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.8.1 引数なしで getline を使う

getline コマンドはカレント入力ファイルから入力するときには 引数を省略することができる。この場合、入力レコードはフィールドに分割される。 この getline の使い方は、カレント入力レコードに対する処理は終わったが、 何か特別な処理を次のレコードに対してその時点で処理を行いたい、 というときに便利である。たとえば

 
{
     if ((t = index($0, "/*")) != 0) {
          # value of `tmp' will be "" if t is 1
          tmp = substr($0, 1, t - 1)
          u = index(substr($0, t + 2), "*/")
          while (u == 0) {
               if (getline <= 0) {
                    m = "unexpected EOF or error"
                    m = (m ": " ERRNO)
                    print m > "/dev/stderr"
                    exit
               }
               u = index($0, "*/")
          }
          # */がファイルの末尾にあるとき
          # substrを含む式は""となる
          $0 = tmp substr($0, u + 2)
     }
     print $0
}

このawkプログラムは入力から`/* ... */'というCスタイルのコメントを取り除く。 `print $0'を他の文に置き換えることによって、 より複雑な処理をコメントアウトされた入力に対して、 ある正規表現にマッチするかどうかを探すなどして行うことができる (このプログラムには隠された問題点がある-- それは一つのコメント終了と開始とが同じ行にあるときにうまく働かないということである)。

この形式のgetlineコマンドはNFNRFNR$0に新しい値をセットする。

NOTE : 新しい$0の値は、後に続いてあるルールのパターン部とテストするために使われる。 このとき、元々の$0の値は失われる。 対照的に next文で新しいレコードを読んだときは、 即座に通常通りの処理を始め、 プログラムの最初のルールから処理を始める。See section next.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.8.2 変数にgetlineの結果を格納する

`getline var' を使って、 awkの入力からの 次のレコードを変数varに読み込むことができる。他の処理は行われない。 この形式のgetlineawkの読込み-チェック-ルールの ループには見つからないで変数に行を読み込んで格納することができる。 以下の例は二行ごとにその二行を入れ替える:

 
{
     if ((getline tmp) > 0) {
          print tmp
          print $0
     } else
          print $0
}

入力として以下のものがあったとすると:

 
wan
tew
free
phore

結果は次のようになる:

 
tew
wan
phore
free

ここではgetline関数は変数NRFNRに (もちろん varにも)セットする目的で使われている。 レコードはフィールドに分割はされず、したがってフィールドの値($0 も含めて)と、 NFの値は変更されない。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.8.3 getlineを使ったファイルからの入力

`getline < file'を使って、 次のレコードをfileというファイルから読み込むことができる。 このfileはファイル名として特定できる文字列の値を持った式である。 `< file'は異なった場所から直接入力を行うのでリダイレクと呼ばれる。 例えば、次のプログラムはレコードの入力を、 カレント入力からのレコードの最初のフィールドが10であったときに、 `secondary.input'というファイルから行う:

 
{
    if ($1 == 10) {
         getline < "secondary.input"
         print
    } else
         print
}

通常の入力ストリームを使わないので、NRFNRの値は変更されない。 しかし、読み込まれたレコードは通常のルールに従ってフィールドに分割され、 $0 および、その他のフィールドの値は変更される。 もちろんNFの値もその例外ではない。

POSIXに従えば、`getline < expression'expression中に `$'以外の演算子が括弧に囲まれないで現れたときには曖昧となる。 たとえば`getline < dir "/" file' は連接演算子が括弧に囲まれずに現れており、 曖昧になっている。そしてこれは、他のawk処理系でも 問題なく使えるようにしたいのなら、 `getline < (dir "/" file)' と記述すべきである。

NOTE : 不幸にも、gawk`"echo " "date" | getline' の ような構成の扱いでつじつまが合っていなかった。 gawk の バージョン 3.1.1 になる前までは、 `(``echo " "date") | getline' として扱われていた。 (これが Unix の awk は振る舞いである。) 3.1.2 から 3.1.5 の間は、```echo `` (``date'' | getline)' の ように振舞われた。 (これが mawk の振る舞いである。) バージョン 3.1.6 を使って開始した際に、以前の振る舞いが復活され た。 要するに、いつも 明白に括弧を挿入して使用すれば、心配する必要は ない。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.8.4 getlineを使ってファイルから変数へ格納

`getline var < file'を使ってファイルfileから読み込み、 変数varにそれをセットする。 先に述べたように、 fileは読み込みに使用するファイルを指定する文字列式である。

この使い方のgetlineはどの組み込み変数も変更せず、 レコードのフィールドへの分解も行われない。ただ単にvarだけが変わる。 例えば、次のプログラムでは入力ファイルを出力するが、 入力中に `@include filename'と書かれたレコードがあったときに、 その様なレコードをfilenameというファイルの内容で置き換える:

 
{
     if (NF == 2 && $1 == "@include") {
          while ((getline line < $2) > 0)
               print line
          close($2)
     } else
          print
}

これは展開する入力ファイルの名前がプログラム中に書かれていないことに注意。 この例では、`@include'がある行の二番目のフィールドで指定される ファイルからデータを持ってくる

close関数は入力中にある`@include'で指定されるファイルの中に 同じものが二度以上指定されているものがあったとしてもきちんと動作することを 保証するために呼ばれている See section 入出力のリダイレクトをクローズする.

このプログラムで一つ欠けているのは、 本当のマクロプロセッサではできるようなネストした `@include'文(インクルードファイルの中にある `@include') を処理できないという点である。 ネストした`@include'文を処理できるプログラムは See section An Easy Way to Use ライブラリ関数.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.8.5 Using getline from a Pipe


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.8.6 getlineを使ったパイプからの入力

コマンドの出力をパイプを通じてgetline`command | getline' のように使って受けることができる。 パイプは単にあるプログラムの出力を他の入力にリンクするだけである。 このケースでは、 commandという文字列はシェルコマンドとして実行され、 その出力がパイプを通じてawkの入力とされる。 この例のgetlineではパイプからレコードを読み込んでいる。 例えば次のプログラムは入力を出力にコピーするが、 ある行が`@execute' で始まっていると、 その行の`@execute'に続く部分をシェルのコマンドとして実行して、 入力をその結果に置き換える:

 
{
     if ($1 == "@execute") {
          tmp = substr($0, 10)
          while ((tmp | getline) > 0)
               print
          close(tmp)
     } else
          print
}

close 関数は入力中の`@execute'のある行で指定されるコマンドに 二つ以上の同一のコマンドが指定されたときに、 一回毎にそのコマンドを実行する様にさせるために呼び出される。 See section 入出力のリダイレクトをクローズする. 入力として以下のものを与える:

 
foo
bar
baz
@execute who
bletch

プログラムの出力はこうなる:

 
foo
bar
baz
arnold     ttyv0   Jul 13 14:22
miriam     ttyp0   Jul 13 14:23     (murphy:0)
bill       ttyp1   Jul 13 14:23     (murphy:0)
bletch

このプログラムはwhoコマンドを実行し、結果を出力する (もしこのプログラムがやっていることを自分でやろうとしたら、 その結果は異なったものとなるだろう。誰がシステムにログインしているかで変わるからだ)。

この形式のgetlineでは、レコードをフィールドに分割し、 NFをセットして$0を再計算する。 このとき、NRFNRは変更しない。

POSIXに従えば、`expression | getline'expression`$'以外のカッコがついていない演算子を含んでいる ときに曖昧である -- たとえば、`"echo " "date" | getline'は カッコ付けされていない演算子を含んでいるので曖昧である。 他のawkの実装でも動くようにしたいのであれば、 `("echo " "date") | getline'のように記述すべきである。

NOTE : Unfortunately, gawk has not been consistent in its treatment of a construct like `"echo " "date" | getline'. Up to and including バージョン 3.1.1 of gawk, it was treated as `("echo " "date") | getline'. (This how Unix awk behaves.) From 3.1.2 through 3.1.5, it was treated as `"echo " ("date" | getline)'. (This is how mawk behaves.) Starting with バージョン 3.1.6, the earlier behavior was reinstated. In short, always use explicit parentheses, and then you won't have to worry.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.8.7 Using getline into a Variable from a Pipe


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.8.8 getlineを使ってパイプからの内容を変数に格納する

`command | getline var'を使ったとき、 commandの出力はパイプを通じてgetlineに渡り、 さらに変数var に格納される。 例えば次に挙げるプログラムでは date ユーティリティを使用して 日付と時刻を読み込み、それをcurrent_timeという変数に格納し、 さらに表示している:

 
BEGIN {
     "date" | getline current_time
     close("date")
     print "Report printed on " current_time
}

この使い方のgetlineでは、どの組み込み変数も変更されず、 レコードのフィールド分割も行われない


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.8.9 getline を使ってコプロセスから入力する

getlineを使ったパイプからの入力は一方向のオペレーションであった。 `command | getline'で起動したコマンドは あなたのawkプログラムに対してだけデータを送る。

反対に、別のプログラムに処理するためのデータを送ってその結果を 読み取りたいということがあるかもしれない。 gawkコプロセス(coprocess)を起動して 双方向の通信を可能にすることができる。 これは`|&'演算子を使って行う。 典型的には、以下の例のように データをコプロセスに書き込んでから結果を読み取る:

 
print "some query" |& "db_server"
"db_server" |& getline

これは問い合わせをdb_serverに送り、その後で結果を読み出している。

NRFNRの内容は変化しない。 なぜなら主入力ストリームは使われていないからだ。 しかしながら、読み取られたレコードは通常のやり方にしたがって フィールドへの分割が行われるので、$0やその他のフィールド、そして NFの内容は変化する。

コプロセスは発展的機能(advanced feature)である。 getlineに関するセクション がここであるので ここで述べている。 コプロセスの詳細はSee section 別のプロセスとの双方向通信


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.8.10 getlineを使ってコプロセスから変数に格納する

`command |& getline var'を使ったとき、 コプロセス commandからの出力は双方向パイプを通じてgetlineに 送られて、varという変数に格納される。

このバージョンのgetlineでは、組み込み変数は変化しないし、 読み込まれたレコードのフィールドへの分割はされない。 唯一変化する変数はvarである。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.8.11 Points to Remember About getline


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.8.12 getlineに関して覚えておくべきこと

以下に挙げるのは、getlineに関して心にとどめておくべき その他のことである:


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.8.13 getline の変種のまとめ

table-getline-variants 以下に八種類のgetlineの使い方とそれぞれの使い方でセットされる 組込み変数を一覧にした。

Variant

Effect

getline

$0, NF, FNR, NR をセットする

getline var

var, FNR, NR をセットする

getline < file

$0NF をセットする

getline var < file

var をセットする

command | getline

$0NF をセットする

command | getline var

var をセットする

command |& getline

$0NFをセットする。これは gawk の拡張

command |& getline var

varをセットする。これは gawk の拡張

Table 3.1: getline Variants and What They Set


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4. Printing Output

もっともよく行われるアクションの一つは、入力の一部もしくは全部を出力することである。 単純な出力のためにprint文を使い、整形して出力するために printf文を使う。 この両者はこの章で説明する。 print文は出力する値を計算するときに限らない。 しかし二つの例外があって、どのように出力するかを 指定すること -- どのくらいのカラムでどのような表記で(指数表記を使うか否か) 出力するかといった ことを指定することができない(二つの例外についてはsee section 出力セパレータprintの数値出力を制御する)。 これらを指定して出力するには、printf文が必要となる(see section printfを使って出力を整える)。

基本と書式付け出力と共に、この章 では ファイルやパイプへの入出力のリダイレクトをカバーし、 さらにgawkが内部的に処理するファイル名 や 組み込み関数closeについても説明する。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.1 print

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

 
print item1, item2, …

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

出力の対象となるアイテムは、文字列や数値定数、 ($1のような)カレントレコードのフィールド、変数、 あるいは任意のawk式である。 数値は文字列へと変換されてから出力される。

引数を持たない単純な`print'文は`print $0'と等価であり、 カレントレコード全体を出力する。空行を出力するには、 `print ""'のように空文字列""を指定する。 テキストのひとかたまりを出力するには、"Don't Panic"のように 一アイテムとして文字列定数を使う。ダブルクォートを使うことを忘れて しまうと、そのテキストはawkの式として扱われて たぶんエラーになってしまうだろう。 任意の二つのアイテムの間にはひとつのスペースが挟まることを 心にとどめておくこと。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.2 print 文の例

print文は少なくとも一行の出力を行うが、 一行に限らない。改行を含む文字列がアイテムの値であった場合、 その改行は文字列の残りの部分と共に出力される。 一つのprint文はこのやり方で任意の行数の出力を行うことができる。

以下に挙げるのは改行が埋め込まれた文字列の出力の例である (`\n'は改行キャラクタを表すのに使ったエスケープシーケンスである。 see section エスケープシーケンス):

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

次の例はファイル`inventory-shipped'の各入力レコードの 最初の二つのフィールドを空白で区切って出力する:

 
$ awk '{ print $1, $2 }' inventory-shipped
-| Jan 13
-| Feb 15
-| Mar 15
…

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

 
$ awk '{ print $1 $2 }' inventory-shipped
-| Jan13
-| Feb15
-| Mar15
…

so that the headings are only printed once: ファイル`inventory-shipped'にあまり馴染みのない人に対しても わかりやすい出力をしよう。最初のヘッダ行で何を出力しているかを明確にしようとして、 月ごとの量($1)と、緑のクレーの積込量($2)のテーブルにヘッダを付け加えている。 これをBEGINパターン (see section 特殊パターン 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 文が作られた理由である (see section printfを使って出力を整える)。printf文の機能の一つは データのカラムをそろえることである。

NOTE : print文やprintf文では、任意のカンマの後に改行を置くだけで 行の継続を行うことができる(see section awk Statements Versus Lines)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.3 出力セパレータ

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

print文による出力全体は出力レコードと呼ばれる。 各print 文は一つのレコードを出力し、 その後に出力レコードセパレータ(もしくはORS)と呼ばれる文字列を出力する。 組込み変数ORSがこの文字列を特定する。 ORS の初期値は"\n"という文字列である。 これは改行であり、したがって、通常は各print文は分割された行を作成する。

OFSORSといった変数に新しい値をセットすることによって、 出力フィールドや出力レコードをどのように分割するかを変更することができる。 通常これを行う場所は、入力がまだ処理されないBEGINルール (see section 特殊パターン BEGINEND) の中である。同じことを、 コマンドライン上で入力ファイル名の前に変数への代入を行うことでもできるし、 `-v'オプションを使うことでもできる (see section コマンドラインオプション)。 次の例では、 各入力レコード中のセミコロンで区切られたフィールドの一番目と二番目を、 各行の後ろに空行を追加して出力する。

 
$ awk 'BEGIN { OFS = ";"; ORS = "\n\n" }
>            { print $1, $2 }' BBS-list
-| aardvark;555-5553
-|
-| alpo-net;555-3412
-|
-| barfly;555-7685
…

ORSが改行を含んでいなければ、 すべての出力は陽に改行を出力しない限りは一行にまとまって出力されることになる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.4 printの数値出力を制御する

print 文を数値を出力するために使った場合、 awkは内部で数値を (その数値を表す)文字列に変換し、その文字列を出力する。 awkはこの変換動作のためにsprintf関数を使用する (see section 文字列操作関数). 現在のところ、sprintf 関数が数値(や文字列)をどのように書式化するかを 記述されている 書式指定(format specification)を受諾し、 数値の書式指定は違った方法が何種類もあるということだけで十分である。 書式の違いは書式制御文字で詳しく述べられている。

組込み変数のOFMTは、出力をおこなうときにprintsprintf を使って数値を文字列に変換するための書式のデフォルト設定を保持している。 OFMTのデフォルトの値は"%.6g"である。 OFMTの値を違ったものにすることによって、 printが数値をどのように出力するかを変更することができる。 簡単な例を挙げよう:

 
$ awk 'BEGIN {
>   OFMT = "%.0f"  # 数値を整数として出力 (丸め)
>   print 17.23, 17.54 }'
-| 17 18

POSIX標準に従えば、OFMTに浮動小数点の変換指定ではないなにかをセットした場合の awkの動作は定義されてない(d.c.) 。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.5 printfを使って出力を整える

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


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.5.1 Introduction to the printf Statement

printf文は以下のような形式である:

 
printf format, item1, item2, …

引数リスト全体をカッコで括ることもできる。 このカッコはリストのいずれかのアイテムが関係演算子`>' を使った式であるときには必要となる。カッコがないと、 その文はリダイレクトと混同される可能性がある (see section printprintfの出力のリダイレクト).

printfprintの違いは引数formatにある。 これは文字列として扱われる式で、 他の引数をそれぞれどのように出力するかを指定する。 これは書式指定文字列(format string)と呼ばれる。

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

printf文は改行を自動的に付加することはせず、 書式文字列で指定されたものだけを出力する。 したがって改行が必要ならば、書式指定文字列にそれを含めなければならない。 OFSORSといった変数に格納されている出力セパレータは printfに対して何の影響も与えない。例えば:

 
$ awk 'BEGIN {
>    ORS = "\nOUCH!\n"; OFS = "+"
>    msg = "Dont Panic!"
>    printf "%s\n", msg
> }'
-| Dont Panic!

ここでは、`+'`OUCH'はメッセージの中に現れない。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.5.2 書式制御文字

書式指定子は`%' で始まり、(printf文でどのように出力するかを指定する) 書式指定文字(format-control letter)で終わる。 書式指定文字は出力する値がどんな種類であるかを指定する。 書式指定子の残りは、どの位のフィールド幅で出力するかというような事を指定する (省略可能な)修飾子である。 以下は書式指定文字のリストである:

%c

数値をASCIIキャラクタとみなして出力する。 `printf "%c",65'`A'を出力する。 文字列を与えた場合は文字列の先頭にあるキャラクタを出力する。

%d, %i

どちらも同じで十進整数を出力する (`%i'はISO Cとの互換性のための仕様である)。

%e, %E

これは数値を科学(または指数)形式で出力する。例えば

 
printf "%4.3e\n", 1950

これは小数点以下を含めて全部で四つの数字からなる `1.950e+03'を出力する (`4.3'は 二つの修飾子(modifiers)であり、詳しくは後述する)。 `%E'`e'の代わりに`E'を出力するものである。

%f

浮動小数点形式で数値を出力する。例えば、

 
printf "%4.3f", 1950

これは小数点以下三桁を伴った四桁のsignificant figuresである `1950.000'を出力する(`4.3' は 二つ修飾子(modifiers)であり、 詳しくは後述する)。

IEEE 754浮動小数点フォーマットをサポートしているシステムでは、 負の無限大を表すフォーマットは`-inf'もしくは`-infinity'で あり、正の無限大は`inf'`infinity'である。 特別な“数値でない”(not a number)値は`-nan'または`nan'である。

%F

%fに似ているけれども、“数値でない”値は 大文字で表される。

%F書式はISO Cに対するPOSIXの拡張である。 全てのシステムでサポートされているわけではない。 サポートされていないシステムではgawk%fを代わりに使う。

%g, %G

科学表記か浮動小数点表記の、いずれか出力するキャラクタ数が少なくなる方で出力する。 結果が科学表記であったとき、 `%G'`E'`e'の代わりに使用する。

%o

This prints an unsigned octal integer. 無符号の八進整数を出力する。

%s

文字列を出力する。

%u

無符号の十進整数を出力する (この書式は marginal なものである。なぜならawkにおける数値は 全て浮動小数点数であって、この書式はCとの誤カンのために提供されている)。

%x, %X

十六進の符号なし整数を出力する。 `%X'`a'から`f'までの代わりに `A'から`F'までを使用する。

%%

これは本当は書式制御文字ではない、しかし`%'の後に続けて書かれた場合には意味がある。 `%%'と並べて書いたときには一文字の`%'を出力し、引数を取らない。 また、すべての修飾子を無視する。

注意 : Cでの最も広い整数型の範囲を越えた値に対する整数書式指定を使ったとき、 gawkは書式指定を`%g'に切り替える。 コマンドラインで`--lint'を指定したとき(see section コマンドラインオプション) gawkはこれを警告する。 他のバージョンのawkは間違った値を出力したり何かへんな動作を したりするかもしれない。(d.c.)


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.5.3 printf の書式の修飾子

書式指定には出力されるアイテムの値と、出力する大きさを制御するために 修飾子(modifiers)を含めることができる。 修飾子は`%'と書式指定文字の間に置かれる。 後で挙げる例では、出力中の空白を"'•' というシンボルで表わしている。 以下は使用することのできる修飾子である(出現することのできる順で):

N$

`$'に続く整数値は位置指定子(positional specifier)である。 通常は、書式指定子は書式指定文字列中に現れた順に引数に対して 適用が行われる。位置指定子を伴った場合、書式指定子は引数リスト中の 次の引数ではなく特定の引数に対して適用される。 位置指定子は1から始まる。したがって:

 
printf "%s %s\n", "don't", "panic"
printf "%2$s %1$s\n", "panic", "don't"

これは有名な友好的メッセージを二度出力する。

一見したところでは、この機能はそれほど使われていないようだ。 事実これはgawkの拡張であり、実行時に翻訳メッセージを使うために 用いられる。 See section Rearranging printf Argumentsにどのように、なぜ位置指定子を使うのかが 説明されている。現在のところ、我々はこれを使わない。

-

マイナス記号は出力幅の修飾に使い、与えられた出力幅の中で左詰めを行うことを指示する。 通常は与えられた出力幅の中で右詰めで表示がなされる。従って、

 
printf "%-4s", "foo"

これは`foo•'を出力する。

空白

数値の変換の際に、その値が正であればスペースを、 負であればマイナス記号を数値の前に置く。

+

プラス記号は幅指定の修飾子(後述する)の前で使用し、 変換された数値が正の数値であっても、その前に必ず符号をつけることを指示する。 `+'はスペース修飾子をオーバーライドする。

#

特定の制御文字に対して"別形式"(alternate form)を使用する。 `%o'の前に置いた場合数値の前にゼロを置く。 `%x'`%X'の前に置いた場合、 出力する数値が0でなかったときにそれに先行して`0x'`0'を置く。 `%e', `%E', `%f' の場合、常に小数点をつけるようにする。 `%g', `%G'の場合、小数点以下の0も省略されないようにする。

0

%に続いた`0'はフラグとして動作し、出力のパディングをスペースではなく 0で行うようにする。 これは数値ではない出力書式のときも適用される(d.c.) 。 このフラグはフィールド幅が実際に出力される値の幅よりも大きい場合にのみ効果を持つ。

'

シングルクォート(またはアポストロフィ)はISO Cに対するPOSIXの拡張である。 浮動小数点数値の整数部もしくは十進整数値全体に対して 1000ごとの区切り文字を入れることを指示する。 これはそれをサポートしているロカールにおいてのみ動作する。 たとえば:

 
$ cat thousands.awk          ソースプログラムを表示
-| BEGIN { printf "%'d\n", 1234567 }
$ LC_ALL=C gawk -f thousands.awk
-| 1234567                   "C" ロカールで実行
$ LC_ALL=en_US.UTF-8 gawk -f thousands.awk
-| 1,234,567                 US English UTF ロカールで実行

ロカールと国際化に関する事柄についてはWhere You Are Makes A Differenceを参照。

注意 : `''はすばらしい機能であるけれども、使うのが複雑である: コマンドラインプログラムにおいて使うことが難しい。 クォーティングトリックに関することはシェルのクォーティングについてを参照。

width

これはフィールドの幅を指定する数字である。 `%' と書式指定文字の間に置かれ、 この幅にフィールドを広げて出力を行うことを指定する。 デフォルトではフィールドを埋めるためのスペースはフィールドの左から置かれる。 次のように指定した場合、

 
printf "%4s", "foo"

prints `•foo'. `•foo'を出力する。

width は(出力の)最小幅であって、最大幅ではない。 アイテムの値が width よりも多くのキャラクタを必要としたならば、 出力はその幅となる。したがって、

 
printf "%4s", "foobar"

これは`foobar'を出力する。

マイナス記号を伴ったフィールド幅指定は、 幅あわせのためのスペースをフィールドの左ではなく右に置く。

.prec

カンマに続く整数値は出力のときを精度を指定するのに使用する数字である。 制御文字で使用する精度を指定する:

%e, %E, %f

小数点の右側に現れる桁数。

%g, %G

数の最大桁。

%d, %i, %o, %u, %x, %X

出力すべき数字の最小桁数。

%s

出力すべき文字列の最大桁数。

したがって、

 
printf "%.4s", "foobar"

これは`foob'を出力する。

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

 
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

これは読み易いとは云えないが、きちんと働く。

Cプログラマはprintfの書式指定文字列中で `l'`L'`h'というフラグを使っているかもしれない。 これらはawkでは不正なフラグである。 大部分のawk処理系ではこれらを何の警告もなしに無視する。 `--lint'がコマンドラインで指定されている場合 (see section コマンドラインオプション)、 gawkはこれらのフラグを使用したときに警告を発する。 同様に`--posix'が指定されている場合には致命的エラーとなる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.5.4 printf を使った例

次の例は、printfを整列したテーブルの出力にどのように 使用するかというものである。

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

このプログラムは `BBS-list'というファイル中のBBSの名前($1) を10文字の文字列として、左寄せで出力する。 同様に電話番号($2)をそれに続いて出力する。 結果として、二つのカラムからなる、名前と電話番号のきちんと並んだテーブルが作成される。

 
$ awk '{ printf "%-10s %s\n", $1, $2 }' BBS-list
-| 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

なぜ電話番号を数値として出力するように指定していないのかわかるだろうか? ここでは数字がダッシュで区切られているので文字列として出力している。 もし、これを数値として出力しようとしたならば、 そこで得られるものは最初の三つの数字 `555'である。 これは混乱を招くだろう。

ここでは電話番号の出力時の幅を指定していない。 それは出力行の最後にあるものであり、その後ろにスペースを置く必要がないからだ。

先頭のカラムにヘッダを付け加えることによってテーブルをより見やすいものにしている。 これを行う為に、BEGINパターン(see section 特殊パターン 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

試しに以前に説明した (see section print). `inventory-shipped'の例の、表題と表のデータを printf文でそろえてみてほしい。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.6 printprintfの出力のリダイレクト

これまではprintprintfからの出力は標準出力。 通常は端末に対してのみ行われていた。 printprintfの両方とも別の場所に出力を送ることができる。 これはリダイレクション (redirection)と呼ばれる。

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

以下に四種類の出力リダイレクションの例、 出力をファイルに送ったり、ファイルに追加したり、 パイプを通して他のコマンドに出力したり、 コプロセスに出力するものを挙げる。 これらの例ではprint文が使われているが、 printf文を使っても同じように動作する。

print items > output-file

この書式のリダイレクションではアイテムの出力をoutput-file というファイルに対して行う。 ファイル名 output-fileは式であってもかまわない。 式を文字列に変換し、その後にファイル名として使用する (see section )。

この書式のリダイレクションは最初の出力を行う前に、 output-fileの削除を実行する。 以後はoutput-file の削除はしないでファイルに追加する形で出力を行う。 もしoutput-file が存在しなければファイルが新たに作成される。 たとえば、次に挙げるawkプログラムはBBSの名前のリストを `name-list'という名前のファイルに、 電話番号のリストを`phone-list'というファイルにそれぞれ出力する。 出力されたファイルの各行は、一つの名前あるいは番号で構成される。

 
$ awk '{ print $2 > "phone-list"
>        print $1 > "name-list" }' BBS-list
$ cat phone-list
-| 555-5553
-| 555-3412
…
$ cat name-list
-| aardvark
-| alpo-net
…

それぞれの出力ファイルは一行に一つの名前もしくは番号が置かれる。

print items >> output-file

このリダイレクションは、出力をoutput-fileというファイルに対して行う。 先ほどの`>'での指定と違うのは、 output-fileの削除が行われないということである。 出力はファイルに追加される形で行われる。 output-fileが存在していなければ、作成される。

print items | command

こうすることによって出力を、ファイルに対して行う代わりにパイプを通して渡す事ができる。 このやり方のリダイレクションは commandへのパイプをオープンし、 そのパイプを通して、 commandを実行する為に作られたプロセスへ itemsの値を出力する。

リダイレクションの引数commandawkの式でも良い。 その式の値はコマンドを実行するための文字列に変換される。 次の例では二つのファイルが作られる。一つはソートされていないBBSの名前のリスト。 もう一つはアルファベットの降順にソートされたリストである。

 
awk '{ print $1 > "names.unsorted"
       command = "sort -r > names.sorted"
       print $1 | command }' BBS-list

この例のソートされていないリストは、ソート済みリストがパイプを通って sortユーティリティが出力する間に、通常のリダイレクトで書き込まれる。

次の例はリダイレクトを使って、 メイリングリスト`bug-system'に従ってメイルを送るものである。 これはトラブルにあったときに、 システムメンテナンスの為に定期的にawk スクリプトを実行するような場合に便利だろう。

 
report = "mail bug-system"
print "Awk script failed:", $0 | report
m = ("at record number " FNR " of " FILENAME)
print m | report
close(report)

メッセージは文字列連接と変数mに保持されている内容を使って組み立てられている。 作成されたメッセージはパイプラインを通してmailプログラムに送られる (カッコは連接すべきアイテムをまとめている -- 文字列連接を参照)。

ここでclose関数を呼んでいるのは、パイプを閉じてすぐに出力を送るためである。 詳しいことは See section 入出力のリダイレクトをクローズする

この例はファイルやコマンドを与えるのに変数を使っている。 つまり常に文字列定数を使う、というわけではないということである。 (もし同じファイルやコマンドを参照するわけでないのなら) awk が毎回同じ文字列値を要求するので、 変数を使うというのは一般にはいいアイデアである。

print items |& command

このタイプのリダイレクションはアイテムをcommandの入力に対して出力する。 `|'によるリダイレクションはcommandの出力を getlineを使って読み出すことができる。 したがって、commandコプロセスであり 呼び出し元と一緒に動作するけれどもawkプログラムの配下にある。

この機能はgawkの拡張であり、POSIX awkでは 使うことができない。 簡単な説明は See section getline を使ってコプロセスから入力する。 より完全な説明はSee section 別のプロセスとの双方向通信

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

最初のprintのファイルへの出力で`>'を使い、 その後の出力に`>>'を使うのはありがちな間違いである:

 
# ファイルをクリアする
print "Don't panic" > "guide.txt"
…
# 追加
print "Avoid improbability generators" >> "guide.txt"

これはシェルにおいてリダイレクションを行うときには必須であるが、 awkにおいては必要ない。 このような場合、出力ファイルはただ一度だけオープンされるので すべてのprint文で`>'を使うべきである。 (予期した順番に出力を行おうとして `>'`>>' を混在して使う場合によくある。 しかしなばら、同じファイルに対してオペレータを混在させることは明らかに貧弱なスタイル であり、プログラムを読む人を困惑させる。)

すでに述べたように (see section Points to Remember About getline)、 多くの 多くの awkの実装ではawkプログラムがオープンできるパイプラインの数が ただ一つに制限されている! gawkでは、そのような制限はない。 使用しているオペレーティングシステムが許すだけパイプラインをオープンすることができる。

Advanced Notes: Piping into sh

特に強力なリダイレクションの使い方というのは、コマンドラインを組み立てて それをシェル(sh)に対してパイプで送るというものである。 たとえば、すべてのファイル名 が大文字で格納されるシステムから ファイルのリストを持ってきてそれをすべて小文字の名前に付け替えたいと しよう。以下のプログラムはそれを効率よくやってくれる:

 
{ printf("mv %s %s\n", $0, tolower($0)) | "sh" }

END { close("sh") }

tolower関数はその引数の中の大文字を全て小文字に変換して 返す(see section 文字列操作関数)。 このプログラムはコマンドラインのリストを構築する。 そしてそのリストをシェルに対して送って実行させる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.7 gawk における 特殊な ファイル名

gawkは内部的に解釈される特殊なファイル名 を数多く提供している。 これらのファイル名 は標準ファイルディスクリプタ、プロセス関係の情報、 TCP/IPネットワーキングに対するアクセスを提供する。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.7.1 標準ディスクリプタのためのスペシャルファイル

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

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

 
print "Serious error detected!" | "cat 1>&2"

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

 
print "Serious error detected!" > "/dev/tty"

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

gawkは三つの標準ストリームにアクセスするために特殊なファイル名 を持っている。 gawkで入出力をリダイレクトするときにリダイレクト先のファイル名が 次の特殊な名前の中にあれば、ファイル名 の代わりに gawkは直接(定義されている)ストリームを使用する。 これらの特殊なファイル名 はgawkが移植されている全ての オペレーティングシステムで動作するが、POSXI準拠のものではない:

`/dev/stdin'

標準入力 (ファイル記述子 0)。

`/dev/stdout'

標準出力 (ファイル記述子 1)。

`/dev/stderr'

標準エラー出力 (ファイル記述子 2)。

`/dev/fd/N'

ファイル記述子 N に結び付けられたファイル。そのファイルは awkを実行したプログラム(典型的にはシェル)によって オープンされていなければならない。 gawkが起動された シェルによって特別なことが行われていない限り、記述子 0, 1, 2 だけが使用可能である。

`/dev/stdin', `/dev/stdout', `/dev/stderr'といった ファイル名 は`/dev/fd/0', `/dev/fd/1', `/dev/fd/2'の 別名である。しかし、より説明的な名前である。 gawkプログラムでエラーメッセージを出力する適切な方法は `/dev/stderr'を以下のようにして使うことである:

 
print "Serious error detected!" > "/dev/stderr"

ファイル名 の周りをクォートでくくっていることに注意。 他のリダイレクションと同様、その値は文字列でなければならない。 クォートを省略してしまうのはありがちな間違いであり、 その場合混乱する結果となるだろう。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.7.2 プロセスに関連した情報のためのスペシャルファイル

gawkgawkプロセスが実行されている情報に アクセスすることのできる特殊なファイル名 も提供している。 これらの“ファイル”は情報のレコードを一つ持っている。 二度以上読み出すには、最初にclose関数でクローズせねばならない (see section 入出力のリダイレクトをクローズする)。 ファイル名 は以下のとおり:

`/dev/pid'

このファイルを読むとカレントプロセスのプロセス ID が 十進形式で改行で終端されて返ってくる。

`/dev/ppid'

このファイルを読むと、カレントプロセスの親プロセスIDが十進数で、 改行で終端されて返ってくる。

`/dev/pgrpid'

このファイルを読むと、カレントプロセスのプロセスグループIDが十進数で、 改行で終端されて返ってくる。

`/dev/user'

このファイルを読むと、改行で終端されスペースで区切られたフィールドを 持つ一つのレコードが返ってくる。そのフィールドは以下のような情報を表しているものである:

$1

getuidシステムコールの返す値(実ユーザーID番号)。

$2

geteuidシステムコールの返す値(実効ユーザーID番号)。

$3

getgidシステムコールの返す値(実グループID番号)。

$4

etgidシステムコールの返す値(実効グループID番号)。

さらに追加のフィールドがあるなら、 getgroupsシステムコールが返したグループIDである (Multiple グループはすべてのシステムでサポートされているわけではない)。

これらの特殊ファイル名 はコマンドライン上でデータファイル のように使ってもよいし、 awkプログラムの中で入出力のリダイレクションで使ってもよい。 ただし、`-f'オプションを伴ったソースファイルとしては使うことはできない。

注意 : これらのプロセス関係の情報を提供する特殊ファイルは今では 時代遅れ(obsolete)なもので、次のgawkのリリースでは 完全になくなってしまうだろう。 gawkはこれらのファイルを使うたびに警告のメッセージを出力する。 プロセス関係の情報を得るためには、配列 PROCINFOを使用する。 See section 情報を伝達する組み込み変数,


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.7.3 スペシャルファイル for Network Communications

gawkのバージョン 3.1から、awkプログラムで 双方向のTCP/IPコネクションをオープンすることが可能となり クライアントやサーバーとして振舞うことができるようになった。 これは以下の形式の特殊なファイル名 を使って行う:

 
`/inet/プロトコル/ローカルポート/リモートホスト/リモートポート'

Full discussion is delayed until プロトコル`tcp', `udp', `raw'のいずれかである。 そしてその他のフィールドは作成するネットワークコネクションのための 情報となる。これらのファイル名 は`|&'演算子を使って コプロセスとの通信を行う(see section 別のプロセスとの双方向通信)。 これは発展的な機能であり、ここでは完全性のためにのみ取り上げている。 完全な説明はUsing gawk for Network Programmingにある。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.7.4 Special ファイル名 Caveats

以下はgawkが提供する特殊なファイル名 を使う際の 注意事項のリストである:


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.8 入出力のリダイレクトをクローズする

もし同じファイル名や同じシェルコマンドが、 awkプログラムの実行中に二度以上getline (see section getlineを使った入力) と一緒に使われた場合、ファイルのオープンやコマンドの実行は最初の一回だけ行われる。 つまり最初にファイルやコマンドからレコードを読み込むときである。 次にgetlineを使って読むときは同一のファイル、コマンドから別のレコードを読み込む。

同様にファイルやパイプが出力のためにオープンされたとき、 awkは(オープンした情報を)そのファイル名やコマンド名と結び付け、 後になって出力したときにその出力をそれまでのものに追加する形で行う。 ファイルやパイプはawkが終了するまでオープンされている。

このことによって、同じファイルを再び最初から読み始めたいときや、 シェルコマンドを再実行(コマンドの出力からもっと読みだしたいとき) したいようなときには特別な手順を踏まねばならない。 つまり、次の例のようにclose関数を使わなければならない。

 
close(filename)

or:

 
close(command)

引数filenamecommandは任意の式であってよい。そ の値は、ファイルをオープンしたりコマンドを起動した文字列と (スペースや他の"irrelevant" (無関係)なキャラクタも含めて) 正確に一致しなければならない。 例えば、次のようにパイプをオープンしたら、

 
"sort -r names" | getline foo

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

 
close("sort -r names")

一度この関数呼びだしが実行されると、 次のファイルやコマンドからのgetlineや、 ファイルやコマンドに対するprintprintfは、 ファイルの再オープンやコマンドの再実行を行う。 ファイルやパイプラインをクローズするのに使う式はそれらの ファイルオープンするときやコマンドを実行するときに使った式と正確に一致しなければ ならないので、ファイル名 やコマンドを変数に格納する練習に良い。 先の例は以下のように記述することができる:

 
sortcom = "sort -r names"
sortcom | getline foo
…
close(sortcom)

これは、awkプログラムに見つけづらい打ち間違いのエラー (typographical errors)が入り込むのを防ぐのに役立つ。 以下に出力ファイルをクローズする必要があるかもしれない理由を幾つか挙げる:

システムがオープンすることを許している以上の数のファイルを使おうした場合、 gawkは使っているデータファイルの中で多重オープンを試みる。 gawkのこれを行う能力はあなたが使っているオペレーティングシステムの 能力に依存する。 したがって、ファイルを使いおわったら常にclose するということがよいやり方であるし、移植性のためによいのである。 事実、多くのパイプを使っているときにはコマンドが終了したら それをクローズすることは効率がよい。たとえば以下の例を考えてみよう:

 
{
    …
    command = ("grep " $1 " /some/file | my_prog -q " $3)
    while ((command | getline) > 0) {
        process output of command
    }
    # ここに close(command) が必要
}

この例はそれぞれのレコード中のデータに基づいて新しいパイプラインを 作る。コメントで示しているようにcloseがなければ、 awkはコマンドを実行するために子プロセスを生成して パイプラインをそれ以上作れなくなってしまうまでファイルディスクリプタを 使ってしまう。

各コマンドが実行終了した(getlineのステータスでファイルの終端が 示された)としても、子プロセスは終了しない(21)。 より重要なことは、パイプのためのファイルディスクリプタは closeが呼び出されるかawkが実行終了するまで 解放されないということである。

closeはファイルを表す引数や リダイレクションによってオープンされたパイプ、子プロセス を表す引数が与えられなかった場合にはなにもしない。

`close(FILENAME)'が コマンドラインで与えられたファイルの名前を通じて読み込んでいる 暗黙のループに対して、“魔術的な” 効果をもっていないことに注意すること。 この場合も、オープンされていないファイルをクローズすることになるので awkはなにもしない。

コプロセスに対して`|&'を使って通信をしているとき、 双方向パイプの片方だけをもう片方をクローズせずにクローズすることは とても便利である。これはcloseに二番目の引数を与えることで 行うことができる。その他のcloseと同じように、 第一引数はコプロセスを起動するのに使用した コマンドの名前か特殊ファイルの名前である。 第二引数は文字列で、その値は"to""from"の いずれかである。大小文字の違いは意味を持たない。 これは発展的機能であるので、詳細は別のプロセスとの双方向通信で行う。

Advanced Notes: Using close's Return Value

Unix 上のawkの多くは、close 関数は実際には文である。 closeからの戻り値を使おうとすると構文エラーとなる: (d.c.)

 
command = "…"
command | getline info
retval = close(command)  # 大部分の Unix の awk では構文エラーになる

gawkcloseを関数として取り扱う。 その戻り値はクローズに成功した時0であり、失敗したときには0以外である。 失敗した場合にはgawkは組み込み変数ERRNOに エラーの発生を示す文字列がセットする。

gawkでは、パイプやコプロセス(の入力か出力)を クローズしたとき、その戻り値はコマンドの終了ステータス である。(22) それ以外の場合、入出力のファイルをクローズしたときの システムのcloseもしくはC 関数fcloseの 戻り値である。この値はクローズに成功したときにはゼロ、 失敗したときは-1である。

POSIX標準はもっと曖昧である。そこではcloseは 成功のときにゼロ、そうでないときに非ゼロを返すとしか書いていない。 一般的には、異なる実装ではパイプをクローズしたときに 異なる値になる。したがって戻り値を移植性のあるものとして 使うことはできない。(d.c.)


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5. 式

式とはawkのパターンとアクションの基本構成ブロックである。 式は出力したり、検査したり、関数に渡す値として評価することができる。 それに加えて、式は代入演算子を使って変数やフィールドの新しい値として代入 代入することができる。

式はパターンやアクションとして使うことができる。 その他のほとんどの文は操作すべき二つ以上の式を有している。 他の言語と同様、awkにおける式は変数、配列の参照、 定数、関数呼び出しとこれらの演算子を使った組み合わせを含んでいる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.1 定数式

もっとも単純な式は定数である。これは常に同じ値を持つ。 定数には三種類あり、数値定数、文字列定数、正規表現定数である

これらはそれぞれ、データの値が必要となったときに適切な文脈で使われる。 数値定数は異なる形式をもつことができるが、内部的には 同一のものとして格納される。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.1.1 数値定数と文字列定数

数値定数は数を意味する。この数は整数でも良いし、 小数でも良いし科学(指数)表記の数であっても良い(23)。 以下に数値定数の例をいくつか挙げる。これらはすべて同じ値を持つ。

 
105
1.05e+2
1050e-1

文字列定数は二重引用符で囲まれたキャラクタの並びから構成される。たとえば、

 
"parrot"

これは`parrot'という構成の文字列である。 gawkにおける文字列は、任意の長さのASCIIのNUL(キャラクタコード 0)も含めた 任意の8bit ASCIIキャラクターを使うことが可能である。 他のawk処理系では一部のキャラクタコードを扱うことが難しいかもしれない。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.1.2 八進数字および十六進数字

awkでは、全ての数値は十進数である。 他のプログラミング言語の多くでは別の基数、八進数であるとか 十六進数を使って数値を指定することができる。 八進数では、数字は0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12 と進む。 `11'は十進数では1掛ける10足す1で`11'であるが、 八進数では1掛ける8足す1でこれは十進数の9に等しい。 十六進数では数字が16個ある。十進では数字は10個しかない(`0'-`9') ので、残りの数字を表すのに`a'から`f'までの文字が用いられる (大小文字の区別はされないので、十六進数字の`a'`A'は 値としては同じである)。したがって、十六進では1掛ける16足す1で十進数の17となる。

ただ単に`11'と書かれているとき、その基数がなんであるかを判別することは できない。したがってCやC++、Cから派生した言語では 基数を識別するための特別な記法がある。 八進数値は先頭に`0'を置き、十六進数値には先頭に `0x'`0X'を置く:

11

十進数値11.

011

八進の11、十進の9。

0x11

十六進の11、十進の17。

以下の例はこれらの違いを明確にする:

 
$ gawk 'BEGIN { printf "%d, %d, %d\n", 011, 11, 0x11 }'
-| 9, 11, 17

八進定数や十六進定数が可能なことは、 バイナリデータのような通常のキャラクタや数値として表現することの できないようなデータを扱うときに便利である。

gawk allows the use of octal and hexadecimal gawkはプログラムテキストの中で八進や十六進の定数を扱うことを 許している。しかしながら、そのような数値は入力データの中にある場合には 異なる扱いを受ける。これはデフォルトで八進や十六進の定数を扱うように してしまうと古いプログラムの動作を変えてしまうからである (もし本当にデフォルト動作を変えたいのなら、`--non-decimal-data' オプションを使用する。see section 十進でない入力データを許す)。 八進や十六進のデータがあるのならstrtonum関数(see section 文字列操作関数) を使ってデータを数値へ変換することができる。 ほとんどの場合、組み込みのビット操作関数(see section gawk のビット操作関数)を 使うときに八進や十六進の定数を使いたくなるだろう。

一部の初期のCの実装と異なり、`8'`9'は正当な八進数値ではない。 gawk`018'を十進の18として扱う:

 
$ gawk 'BEGIN { print "021 is", 021 ; print 018 }'
-| 021 is 17
-| 18

ソースコード中の八進や十六進の定数はgawkの拡張である。 gawkが互換モード(see section コマンドラインオプション)のときには 使うことはできない。

Advanced Notes: A Constant's Base Does Not Affect Its Value

数値定数を内部表現に一度変換してしまうと、gawkは もともとの定数がどの基数で記述されたものであるかは記憶していない。 内部的な値が常に使われる。これは数値から文字列への変換のときに 重要である:

 
$ gawk 'BEGIN { printf "0x11 is <%s>\n", 0x11 }'
-| 0x11 is <17>

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.1.3 正規表現定数

正規表現定数は/^beginning and end$/のようにスラッシュで囲まれて記述されている 正規表現である。awkで使われている大部分の正規表現は定数であるが、 `~'`!~'といったマッチング演算子は "動的な" 正規表現(正規表現を含む普通の文字列や、変数)に使うこともできる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.2 正規表現定数を使う

`~'`!~'といった演算子の右側で使われたとき、 正規表現定数は単にマッチングを試みる正規表現を意味するだけである。 しかしながら、(/foo/のような)正規表現定数は単純式のように使われる場合がある。 正規表現定数がそのままで現れたとき、それは`($0 ~ /foo/)'であったのと 同じ意味になる (d.c.) See section パターンとしての式。 このことは、

 
if ($0 ~ /barfly/ || $0 ~ /camelot/)
    print "found"

 
if (/barfly/ || /camelot/)
    print "found"

がまったく同じであるということである。 このルールによる一つの奇妙な結果は、 以下のブール式が、正当であるがユーザーが望んだものではないという ものである。

 
# /foo/ が ~ の左側にあることに注意
if (/foo/ ~ $1) print "found foo"

このコードは"明らかに"$1/foo/という正規表現との マッチングを行なうように見える。 しかし実際にはこの`(/foo/ ~ $1)'という式は、 `(($0 ~ /foo/) ~ $1)' と同じである。 言い換えるならば、最初に入力レコードに対して /foo/という正規表現を適用する。 その結果は0か1のどちらかであり、マッチの成否によって決定する。 その結果に対してレコードの第一フィールドを適用してマッチングを行なう。 こういった動作は本当にユーザーが望んでいたテストとは違ったものであろうから、 こういった文をプログラム中で見つけると、gawkは警告を発する。 もうひとつ、この規則による結果は代入文でも起きる。

 
matches = /foo/

assigns either zero or one to the variable matches, depending upon the contents of the current input record. This feature of the language has never been well documented until the POSIX specification. この例では変数matchesに、 カレント入力レコードの内容によって0か1のどちらかが代入される。 言語のこの機能は、POSIX標準までドキュメント化されなかった。

正規表現定数は gensub, sub, gsub といった関数の第一引数として使用され、 また、match関数の第二引数として使われる (see section 文字列操作関数)。 gawkも含めて最近のawk処理系では、 split関数の第三引数として正規表現定数を許している。 一部の古い処理系ではそうではない (d.c.) これは、ユーザー定義関数 (see section ユーザー定義関数) の引数として正規表現定数を使おうとしたときに混乱を引き起こす。例を挙げると、

 
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にそれを通して subgsubに渡される正規表現定数を渡そうとしている。 しかしながら、実際の動作ではpatパラメータは1か0になり、 それは/hi/$0とマッチするかどうかに依存する。 これはおそらくユーザーが本当に渡そうとした値とは異なったものであるから、 gawkはユーザー定義関数に対するパラメータに正規表現定数を 使おうしたときに警告を発する。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.3 変数

変数とはプログラムのある箇所で、 後から別の箇所で参照するために値を格納する方法である。 ユーザーは変数をプログラムのテキストの中で自由に扱うことができ、 また、変数に対する値の代入をawkのコマンドラインで行うことができる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.3.1 プログラム中での変数の使用

変数にはあとでその値を参照するために名前が付けられている。 これまでにも多くの例で変数が出てきた。 変数の名前は文字、数字、あるいはアンダースコアの並びでなければならず、 かつ、先頭が数字であってはいけない。 変数名の大小文字は区別され、したがってaAは別の変数である。

変数名はそれ自身が、変数の現在の値を表わす一つの正しい式である。 変数は代入演算子 (assignment operators)や 増加演算子 (increment operators)、 減少演算子 (decrement operators) によって新たな値を与えられる。See section 代入式.

awk. すべての組み込み変数の名前は全体が大文字である。 一部の変数はあらかじめ特別な意味を持っているものがある。 フィールドセパレータとしてのFSや カレント入力レコード中のフィールド数を表わすNFなどがそれである。 See section 組み込み変数. にそういった変数のリストがある。 これら組み込み変数は他の変数のように代入したり値を参照したりできるが、 その値は自動的にawkが変更したり、使ったりする。 組み込み変数の名前はそれぞれ大文字だけからなる。

awkでの変数は数値でも文字列でも代入することができる。 デフォルトでは文字列は空文字列で初期化され、 それが数値に変換されるときには 0として扱われる。 このことは、awkではCやその他の多くの言語のように 変数の初期化を陽に行う必要がないということである。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.3.2 コマンドライン上で変数に代入する

awkを起動したときにコマンドライン上の引数に 変数代入(variable assignment) を含めることによって、 任意のawkの変数に値をセットすることができる (see section その他のコマンドライン引数)。代入は次のような形式である。

 
variable=text

こうすることによって、変数に対する値のセットをawkの起動時か 入力ファイルと入力ファイルの間で行なうことができる。 ここで、代入の前に`-v'というオプションを次のように付けた場合、

 
-v variable=text

変数に対する値のセットは一番最初、BEGINルールが実行される前に行なわれる。 `-v'オプションとその後の代入はすべてのファイル名の引数の前になければならず、 またプログラムテキストがコマンドラインで与えられている場合にも、 それの前になければならない (`-v'オプションについての詳細はSee section コマンドラインオプション)。 そうでない場合には、変数に対する代入は前に位置している ファイル名を示す引数が処理された後で行なわれる。たとえば、

 
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 という名前の配列によって参照することができる (see section ARGCARGV を使う)。 awkはエスケープシーケンスのためにコマンドラインでの代入の 値を処理する(see section エスケープシーケンス)。 (d.c.)


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.4 文字列と数値の変換

awkプログラムの文脈により、必要に応じて文字列を数値に変換したり数値を文字列に 変換したりする。例えば、`foo + bar'という式があったときに、 foobarの値(もしくは両方)が文字列であった場合、 その値は加算が行われる前に数値に変換される。 逆に文字列連結中で数値があった場合にはそれは文字列に変換される。例を挙げると、

 
two = 2; three = 3
print (two three) + 4

この例は数値の27を出力する。変数 twothree の数としての値は文字列に変換され、それが連結される。 その結果得られた文字列は再度 23という数に変換され、それに4が加えられる。

何かの理由で数を強制的に文字列に変換したいときは空文字列""をくっつける。 逆に文字列を強制的に数に変換するには、文字列に0を加える。 文字列の頭の方に数字列がついているような文字列は文字列から数値に変換される。 "2.5"は2.5に、"1e3"は1000に変換され、 "25fix"は数の値として25を持つ。 正しい数として認識できない文字列は0に変換される。

数値の文字列への変換のルールは、awkの組み込み変数CONVFMT (see section 組み込み変数)によって制御される。 数値は関数sprintfに書式文字列として CONVFMTが渡されたのと同じ様に変換される(see section 文字列操作関数)。

CONVFMTのデフォルトの値は"%.6g"であり、 これは値を最低6文字あるとして変換する。 一部のアプリケーションのためにこの精度を変更したいと考えるかも知れない。 最近のマシンの大部分では倍精度実数は十進数で17桁あれば (ほとんどの場合は)十分に収めることができる。(24)

CONVFMTsprintfで通常の浮動小数点数を 指定しているような文字列ではないものをセットした場合、 おかしな結果となるだろう。 例えば書式文字列の`%'を忘れた場合には、全ての数値は同じ文字列定数に変換される。 特別な場合として数値が整数であった場合、 変換した結果の文字列は常に整数のものとなり、 CONVFMTの値に左右されない。 次のようなプログラムの断片があったとすると、

 
CONVFMT = "%2.2f"
a = 12
b = a ""

b"12.00"ではなく"12"という値を持つ。 (d.c.)

POSIX の標準以前には、awkは数値から文字列への変換の為に OFMTの値を使用していた。 OFMTprint文で数値を出力するときの書式を指定する。 CONVFMTは、出力するときと変換するときを区別するために導入された。 CONVFMTOFMTの両方とも、デフォルトの値は"%.6g" である。 多くの場合、古いawkはその挙動を変えないだろう。 しかし、この様なOFMTの使用は、 プログラムを他の実装による awk処理系に持っていくような場合には、 常にそのことに注意せねばならない。こういった場合、 プログラムを直すよりもgawkそれ自体を移植することを勧める。 print文に関する詳しい説明はSee section print

そして、もう一度、数値と文字列の間の変換について述べておこう。 Where You Are Makes A Differenceにおいて、地域ごとのキャラクターセットと言語(ロカール(locale))が gawkのマッチするキャラクタに影響することに言及した。 特にawkプログラムに対しては小数点を表すキャラクタに影響する。 "C"ロカールとほとんどの英語圏のロカールでは小数点としてピリオド(`.') を使っている。しかし、多くのヨーロッパのロカールや非英語圏のロカールでは 小数点としてカンマ(`,')を使っている。

POSIX標準はawkawkプログラムのソースコードを読むときや コマンドライン引数(see section その他のコマンドライン引数)で代入を行うときには 常にピリオドを小数点として使用するとしている。 しかし、入力データを解釈するときやprintprintfで出力するとき、 数値から文字列への変換の際にはその地域での小数点キャラクタが使われる。 GNU/Linux システムで、挙動の違いを示すいくつかの例を挙げておく。

 
$ gawk 'BEGIN { printf "%g\n", 3.1415927 }'
-| 3.14159
$  LC_ALL=en_DK gawk 'BEGIN { printf "%g\n", 3.1415927 }'
-| 3,14159
$ echo 4,321 | gawk '{ print $1 + 1 }'
-| 5
$ echo 4,321 | LC_ALL=en_DK gawk '{ print $1 + 1 }'
-| 5,321

`en_DK'ロカールはデンマークにおける英語のロカールであり、 小数点としてカンマを使っている。通常の"C"ロカールでは gawk`4,321'`4'として扱うが、 Danishロカールではそれは全て数値として扱われ`4.321'となる。

バージョン 3.1.3 から 3.1.5 の間、gawk は標準では完全 にコンパイルすることができた。 しかしながら、英語圏外のロカールの多くのユーザーは、小数点としてピリオ ドを使っているので、この挙動について不可思議に思っている。 バージョン 3.1.6 の最初は、デフォルトの挙動は小数点文字としてピ リオドを使うように復活させた。 `--use-lc-numeric' オプション (コマンドラインオプション 参照) を使うこ とで gawk がそのロカールの小数点文字を強制的に使えるようにな る。 (gawk は、 `--posix' または POSIXLY_CORRECT 環 境変数のどちらかが設定されることで、POSIX モードでロカールの小数点文字 を扱うことにもなる。)

The following table describes the cases in which the locale's decimal point character is used and when a period is used. Some of these features have not been described yet.

Feature

Default

`--posix' or `--use-lc-numeric'

`%'g'

Use locale

Use locale

`%g'

Use period

Use locale

Input

Use period

Use locale

`strtonum'

Use period

Use locale

Table 5.1: Locale Decimal Point versus A Period

最後に、最近の公的規格や IEEE standard floating point representation では標準的ではないが、gawk はいくつかの特殊文字の値を数値に 変換するようなことは重要な効果がある。 詳細は標準と実際 を参照のこと。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.5 数学的演算子

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

べき乗。xy乗する。 `2 ^ 3'の値は8になる。 `**'というキャラクタの並びは`^'と等価である。

x * y

乗算。

x / y

割り算。awkのすべての数値は浮動小数点数であるので、 `3 / 4' の結果は整数に丸められることはなく、0.75となる (これは特にCのプログラマーにおいてよくある間違いで、 awkにおけるすべての数は浮動小数点数であるという ことと整数であるように見える定数の割り算も整数ではなく実数が結果となる ということを忘れてしっているのである)。

x % y

剰余。詳しいことはこのリストの後で述べる。

x + y

加算。

x - y

減算。

単項のプラスとマイナスは同じ優先順位を持ち、 乗算演算子はすべて同じ優先順位を持つ。 また、加算と減算は同じ優先順位を持つ。

x % yの剰余を求めるとき、 その商は整数にゼロ方向へ丸められ、さらにyが掛けられる。 この結果はxから減算される。 この操作は"trunc-mod"として知られている。 以下の関係が常に保たれる。

 
b * int(a / b) + (a % b) == a

一つの望ましくないであろうこの剰余の定義の効果は、 xが負であるときにx % yが負になるということである。 したがって:

 
-17 % 8 = -1

他の実装のawkでは、剰余の符号はマシン依存である。

注意 : POSIX標準ではべき乗に対して`^'しか定義していない。 移植性を最大限に保つには、`**'を使わないこと。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.6 文字列連接

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
…

文字列の連接が明確な演算子を持っていないので、 連接を行なうアイテムをカッコで囲んで連接が確実に行なわれることを 保証することがしばしば必要となる。 たとえば次のようなコードではfilename を連接するということが明確になっていない。

 
file = "file"
name = "name"
print "something meaningful" > file name

こうして Unix の awk の文法エラーが発生する。 (25) これは次のように書く必要がある。

 
print "something meaningful" > (file name)

連接を使うときには、いつでもカッコで連接の全体を囲むことを勧める (とくに`='の右辺のオペランドであったりしたときには)。 文字列連接を使った式には十分注意すること。 とくに、連接に使われる式の評価の順番がawk言語において 明確になっていない場合には。以下の例を考えてみよう。

 
BEGIN {
    a = "don't"
    print (a " " (a = "panic"))
}

ここで、aに対する代入が連接により得られる値の取得の前に行われるのか 後に行われるのか定義されていない。この結果は `don't panic'`panic panic'のいずれかになる。 他の演算子と混ぜて使うとき、連接の優先順位はしばしば直感に反したものとなる。 以下の例を考えてみよう。

 
$ awk 'BEGIN { print -12 " " -24 }'
-| -12-24

これは"明らかに""、-12、スペース、-24を連結している。 しかしスペースはどこに行ってしまったのだろうか? その答えは演算子の優先順位の組み合わせとawkの自動変換規則に 隠されている。望みの結果を得るためには、プログラムを以下のように書く。

 
$ awk 'BEGIN { print -12 " " (-24) }'
-| -12 -24

これはawk`-24'`-'を単項のものとして扱うように 強制する。そうしなければ以下のように解釈される。

 
    -12 (" " - 24)
⇒ -12 (0 - 24)
⇒ -12 (-24)
⇒ -12-24

先に述べたように、連接を行うときにはカッコをつけよう。 さもなければ結果は保証されない。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.7 代入式

代入(assignment)はある変数に新しい値をセットする式である。 次の例では、zという変数に1を代入している。

 
z = 1

この式が実行された後では変数zの値は1になり、代入前のzの値は失われる。

文字列も同じ様に代入を行うことができる。 例えば、次の例では messageという変数に"this food is good" という値が格納される。

 
thing = "food"
predicate = "good"
message = "this " thing " is " predicate

この例はまた、文字列の連接も説明している。 `='代入演算子(assignment operator)と呼ばれる、 演算子の中でもっとも単純なもので、右辺の値をそのまま変更せずに代入する。 大部分の演算子(加算、連接、など)は、値を計算する以外になんの作用も持たない。 もし、(計算された)その値を無視するならば、 それは演算子を使わなかったのと同じ結果となるだろう。 しかし代入演算子は異なっていて、 値を生成しその値を無視したとしても代入操作は変更された値を自分に格納する。 これは副作用(side effect)と呼ばれる。

代入の左辺は変数(see section 変数)を必ずしも必要とせず、 それはフィールド (see section フィールドの内容を変更する) や 配列の要素 (see section awk における配列)であってもよい。 これらは総称してlvalues (左辺値)と呼ばれ、 代入演算子の左辺に置くことができる。 右辺にはどのような式、特定の値を代入する式やフィールド、配列要素でも (rvalues(右辺値)と呼ばれるような値を)、置くことができる。

重要なことは、変数は常に同じ型であるというわけではないという事である。 変数の型は単純に、その時点で格納している値によって決定される。 次に挙げるプログラムの断片では、変数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 = 5

この例では三つの変数(x, y, z)全てに、5を格納する。 なぜなら、`z = 5'の値は5であり、それがyに格納され、 さらに`y = z = 5'の値、つまり5が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関数はまだ説明していない。詳しくは See section awk における配列数値関数を参照)。 この例は代入演算子に関する重大な事実を如実にしている。 それは代入演算子の左辺がただ一度しか評価されないということである。 左辺と右辺のどちらの式が先に評価されるのかは処理系による。 以下の例を考えてみよう。

 
i = 1
a[i += 2] = i + 1

この例のa[3]の値は2にも4にもなりうる。

table-assign-opsは算術代入演算子のリストである。 それぞれ、右側のオペランドに現れた式は数値に変換される。

Operator

Effect

lvalue += increment

incrementlvalue に足す。

lvalue -= decrement

decrementlvalue から引く。

lvalue *= coefficient

lvaluecoefficient を掛ける。

lvalue /= divisor

lvaluedivisor で割る。

lvalue %= modulus

lvaluemodulus での剰余をセットする。

lvalue ^= power

lvalue **= power

lvaluepower 乗する。

Table 5.2: 算術代入演算子

注意 : POSIXでは`^='演算子だけが定義されている。 移植性を最大限に保つために、`**=*'演算子は使わないようにすること。

Advanced Notes: `/=' と正規表現との間の構文的な曖昧性

`/='代入演算子と最初のキャラクタが`='である 正規表現定数との間には構文的な曖昧さがある。 (d.c.) これは商用のawkで最も明確に現れている。 たとえば

 
$ awk /==/ /dev/null
error--> awk: syntax error at source line 1
error-->  context is
error-->         >>> /= <<<
error--> awk: bailing out at source line 1

回避策は以下のとおり:

 
awk '/[=]=/' /dev/null

gawkはこの問題や 他の自由に入手することのできる awk 処理系にある他の自由に入手することのできる処理系に あるような問題を抱えていない。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.8 インクリメント/デクリメント演算子

インクリメント演算子(increment operators)や デクリメント演算子(decrement operators) は変数の値を1増やすか、1減らすかする。 この演算子は代入と同時に使うことができ、 インクリメント演算子はそういった場合にawk言語に対して 何の影響も及ぼさないが、非常によく行なわれることを省略して記述することができる。

1を加える演算子は`++'と記述する。この演算子は変数のインクリメントを、 その値を得る前、得た後のどちらでも行える。 変数vに対して先にインクリメントを行うには`++v'と記述する。 これはvの値に1を加えて、新しい値がこの式の値となる。 `v += 1'という代入式はこれと全く同じである。 変数の後に`++'を記述したものはポストインクリメントと呼ばれる。 これは変数の値をインクリメントするが、先ほどと違って、 インクリメント式自体の値は変数の古い値である。 従って、fooの値が4のとき、`foo++'という式の値は4 であるが foo の値は5に変更されている。 言い換えると、この演算子は変数の古い値を返すが副作用としてインクリメントを 行うということである。

ポストインクリメントの`foo++'`(foo+= 1) - 1' という記述とほぼ同じであるが、完全に同じというわけではない。 それは、awkでの数値は全て浮動小数点数だからである。 浮動小数点数では、 `foo + 1 - 1'fooと同一ではない。 しかし、かなり小さな数(10の12乗、つまり一兆未満) しか扱わない限りはこういった違いはごく僅かである。

フィールド、及び配列要素は変数と同じ様にインクリメントが行われる (フィールドの参照と、変数のインクリメントを同時に行いたいときには `$(i++)'を使う。このカッコはフィールド参照演算子`$' の優先順位のために必ず付けなければならない)。

デクリメント演算子`--'`++'と同じ様に動作するが、 加算ではなく、1 を減算する。`++'と同じく、 lvalueの前に置いてプリデクリメントで使用したり、 lvalueの後ろに置いてポストデクリメントで使用することができる

++lvalue

この式はlvalueをインクリメントし、インクリメント後の値をこの式の値とする

lvalue++

この式もlvalueの内容をインクリメントするが、 式の値はインクリメントする前の古いlvalueの値となる。

--lvalue

`++lvalue'と似ているが、加算ではなく減算を行う。 lvalueを減じ、その結果が式の値となる。

lvalue--

`lvalue++'と似ているが加算ではなく減算を行う。 lvalueを減じ、式の値はデクリメントする前の古い値となる。

Advanced Notes: Operator Evaluation Order

Advanced Notes: 演算子の評価順序

Doctor, doctor! It hurts when I do this!
So don't do that!

Groucho Marx

以下のようにしたとき何が起こるだろうか?

 
b = 6
print b += b++

あるいは次のものでは?

 
b = 6
b += ++b + b++
print b

言い換えれば、後置演算子(`b++')による副作用が効果をもつのはいつか? 副作用が起きるのは実装定義である。 言い換えれば、それはawkの特定のバージョンに依存するものである。 最初の例の結果は12か13であり、二番目の例の結果は22か23になるだろう。

まとめると、こういうことをするのは勧められないし 移植性のためにはその動作に頼るべきではない。 あなたのプログラムからはこういったことは取り除いた方が良い。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.9 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.)


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.10 変数の型付けと比較式

The Guide is definitive. Reality is frequently inaccurate.
The Hitchhiker's Guide to the Galaxy

他のプログラミング言語とは異なり、awkの変数は固定した型を持っておらず、 なにから代入されたかによって数値にも文字列にもなる

We look now at how variables are typed, and how awk compares variables.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.10.1 String Type Versus Numeric Type

1992年のPOSIX標準では、数値文字列(numeric string)の概念が導入された。 これは" +2"のような、数値のように見える文字列である。 この概念は変数の型を決定するのに使われる。 変数の型は二つの変数をどのように比較するのかを決定するのに影響するので、 非常に重要である。 gawkでは、変数の型は以下のルールに従う。

この最後のルールは特に重要である。次のプログラムでは、 aは文字列演算の後でも数値タイプを持つ。

 
BEGIN {
         a = 12.345
         b = a " is a cute number"
         print b
}

二つのオペランドを比較するときは、オペランドの属性によって文字列演算子か 数値演算子のいずれかが以下の一覧に従って決定され、使用される。

 
        +----------------------------------------------
        |       STRING          NUMERIC         STRNUM
--------+----------------------------------------------
        |
STRING  |       文字列          文字列          文字列
        |
NUMERIC |       文字列          数値            数値
        |
STRNUM  |       文字列          数値            数値
--------+----------------------------------------------

基本的な考え方は、ユーザーの入力が数値のように見え、 ユーザーが入力だけ行ったときのみ数値であり、 そのデータがキャラクタから構成されていた 場合にはそれは文字列であるというものである。 したがって、例えば、プログラムソースコード中に出現する " +3.14"という文字列定数は -- 数値のように見えるが -- 文字列であり、比較のために数値として扱われることはない

まとめると、一つのオペランドが文字列定数のような"純粋な"文字列であるとき、 文字列比較が行われる。そうでなければ数値比較が行われる。 (26)

This point bears additional emphasis: All user input is made of characters, and so is first and foremost of string type; input strings that look numeric are additionally given the strnum attribute. Thus, the six-character input string ` +3.14' receives the strnum attribute. In contrast, the eight-character literal " +3.14" appearing in program text is a string constant. The following examples print `1' when the comparison between the two different constants is true, `0' otherwise:

 
$ echo ' +3.14' | gawk '{ print $0 == " +3.14" }'    True
-| 1
$ echo ' +3.14' | gawk '{ print $0 == "+3.14" }'     False
-| 0
$ echo ' +3.14' | gawk '{ print $0 == "3.14" }'      False
-| 0
$ echo ' +3.14' | gawk '{ print $0 == 3.14 }'        True
-| 1
$ echo ' +3.14' | gawk '{ print $1 == " +3.14" }'    False
-| 0
$ echo ' +3.14' | gawk '{ print $1 == "+3.14" }'     True
-| 1
$ echo ' +3.14' | gawk '{ print $1 == "3.14" }'      False
-| 0
$ echo ' +3.14' | gawk '{ print $1 == 3.14 }'        True
-| 1

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.10.2 Comparison Operators

比較式(comparison expression)は文字列あるいは数値の、 等しいかなどの比較を行う。 この式はCのスーパーセットになっている関係演算子 (relational operators) を使用する。table-relational-opsで説明する。

Expression

Result

x < y

xy より小さいときに真。

x <= y

xy 以下のときに真。

x > y

xy より大きいときに真。

x >= y

xy以上のときに真。

x == y

xyと等しいときに真。

x != y

xyと等しくないときに真。

x ~ y

文字列x が正規表現 yとマッチするときに真。

x !~ y

文字列x が正規表現 yとマッチしないときに真。

subscript in array

配列arraysubscriptで添え字付けされる要素を持っているときに真。

Table 5.3: 関係演算子

比較式は、真のときその値として 1 を持ち、偽のとき 0 を持つ。 比較式のオペランドが異なる型であった場合、数値オペランドは CONVFMTの値を使って文字列に変換される (see section 文字列と数値の変換)。

文字列の比較は文字列の先頭からキャラクタごとの比較で行なわれる。 したがって、 "10" "9"よりも小さい、ということになる。 二つの文字列が他方の前の部分に等しいような場合、 つまり "abc""abcd"の比較では、 短い方の文字列 "abc" がより小さい、と認識される。

次の例は、非常にありがちな、`=='演算子の`='を一つ忘れてしまうというものである。 これは正しいawkプログラムであるが、期待した動作はしないだろう。

 
if (a = b)   # おおっと! a == b であるべき
   …
else
   …

bが0か、空文字列でない限り、i fの条件式は常に真になる。演算子がこのように似ているために、 この種のエラーはソースコードを眺めたときに見つけることは非常に難しい。

以下に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となる。

`~' 演算子や `!~'演算子の右側にくるオペランドは 正規表現定数(/…/)でもあり、通常の式でもある。 これは、ちょうど文字列としての式の値が 動的正規表現 (see section 正規表現の使い方とsee section 動的正規表現を使う) であるということと同じである

最近のawkの処理系ではスラッシュで囲まれた正規表現定数はそれ自体が式であり、 /regexp/という正規表現は以下の比較式の省略形である。

 
$0 ~ /regexp/

/foo/`$0 ~ /foo/' の省略形にならない特別な場所は、 `~'演算子や`!~'の右辺のオペランドである。 詳しくはsee section 正規表現定数を使うで述べられている。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.11 ブール式

ブール式(Boolean expression)は比較式やマッチング式を論理演算子 "または"(`||') や "かつ"(`&&')、 否定(`!') を用いて繋げたものである。 論理式全体の真偽は各部分式の真偽値の組み合わせによって決定される。 ブール式は論理式でもあり、これら二つの単語の意味は同じである。

ブール式は、比較式やマッチング式を記述できるところであればどこでも 記述することができるし、ifwhiledofor といった文のなかで使うこともできる (see section アクション中の制御文)。ブール式は真のとき1、偽のとき0の値を持ち、 その結果を変数に格納したり算術式に使うこともできる。

それに加えて、それぞれのブール式は正しい論理パターンでもあるので、 ルールの実行を制御するパターンとして使うこともできる ブール演算子には以下のものがある。

boolean1 && boolean2

boolean1boolean2の両方ともが真のときに真となる。 たとえば、次の例ではカレント入力レコードに`2400'`foo' の両方が含まれているときにそのレコードが出力される。

 
if ($0 ~ /2400/ && $0 ~ /foo/) print

部分式boolean2は、boolean1が真であったときにのみ評価が行なわれる。 このことによって、boolean2が副作用を持つ式であったときに 違った結果になるようにすることもできる。 たとえば `$0 ~ /foo/ & ($2 == bar++)'といった式では、 `foo'がレコード中に存在しなかった場合には変数barはインクリメントされない。

boolean1 || boolean2

boolean1boolean2の少なくともどちらか一方でも真であれば 論理式全体が真となる。たとえば、次の例では `2400'`foo'どちらか一方、あるいはその両方が 入力ファイル`BBS-list'からの入力レコード中にあったときに そのレコードを出力する。

 
if ($0 ~ /2400/ || $0 ~ /foo/) print

部分式boolean2は、boolean1が偽であったときにだけ評価が行なわれる。 このことにより、boolean2が副作用を持っていたときには 異なった結果をもたらすこともある。

! boolean

booleanが偽であるときに真となる。 たとえば、次の例では HOME環境変数が未定義であるときに`no home!'を 出力する。

 
BEGIN { if (! ("HOME" in ENVIRON))
               print "no home!" }

in演算子は配列要素への参照で説明されている。

`&&'演算子や`||'演算子は、 その動作から短絡評価 (short-circuit)演算子と呼ばれる。 一部分を評価することで全体の値を決定できる場合には、 式全体の評価は"短絡"(short-circuited)される。

`&&'`||'の後に改行を置くことによって、行の継続をすることができる。 しかし、改行をこれらの演算子の前にバックスラッシュによる行継続なしで置くことはできない (see section awk Statements Versus Lines)。

`!'演算子を使った式の値は1か0のいずれかになる。 これは演算子を適用した式の真偽の値による。 `!'演算子はフラグ変数の意味を真から偽に変えたり、 逆方向に変えるのによく使われる。 例えば、次のプログラムは特殊なパターン行の間の行を出力するものである。

 
$1 == "START"   { interested = ! interested; next }
interested == 1 { print }
$1 == "END"     { interested = ! interested; next }

interestedという変数は、awkのすべての変数と同様に 初期値0(偽)でスタートする。 最初のフィールドが`START'であるような行を見つけたとき、 interestedの値は `!'を使って真へと変わる。 最初のフィールドが`END'であるような行を見つけたとき、 interestedは偽へと戻る。

注意 : next文はnextで説明されている。 nextawkに対して残りのルールをスキップして 次のレコードを取り込み、そしてルール群の先頭から処理を始めるように 指示する。これを使っているのは`START'`END'の行を 出力させないためである。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.12 条件式

条件式(conditional expression)は三つのオペランドをとる特殊な式である。 この式は、ある一つの式の値によって二つの異なった式のうちどちらか一つを 選択するのに使用できる。 条件式はCのそれと同じ様に次のような形で記述される。

 
selector ? if-true-exp : if-false-exp

三つの部分式がある。 三つの部分式のうち最初のselectorは常に最初に計算され、 その値が"真" (0でなく、nullでもない)であれば、 次に if-true-expを計算してその値が式全体の値となる。 "真"でなかった場合には、次にif-false-expを計算し、 その値が式全体の値となる。 例えば、次の式はxの値の絶対値を作る。

 
x >= 0 ? x : -x

条件式が計算される毎に、if-true-expif-false-exp のどちらか一つだけが計算され、もう一つは無視される。 このことは式が副作用を持っているときには重要なことである。 例えば、次に挙げる条件式は配列aもしくはb の添字i の要素を取りだし、iをインクリメントする。

 
x == y ? a[i++] : b[i++]

この例ではiのインクリメントを一回しか行わない。 それは、この式全体が実行される度に、 二つのインクリメント式のうちどちらか一つが実行され、 もう片方は実行されないからである。 配列に関する詳しい説明はSee section awk における配列

gawkのちょっとした拡張として、`?:'を使って、 そのいずれかのキャラクタの直後に改行を置くことによって文の継続を行うことができる。 しかし、これらのキャラクタの前に改行をバックスラッシュによる継続抜きで 置くことはできない (see section awk Statements Versus Lines)。 `--posix'が指定されている場合 (see section コマンドラインオプション)、この拡張は抑制される。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.13 関数呼び出し

関数(function)とは計算を特定する名前である。 名前があるのでプログラム中の任意の場所で呼び出すことができる。 例えば、sqrt関数は数値の平方根を計算する

ある固有の関数群は組み込み(built-in)であり、 全てのawkプログラムで使うことができる。 sqrt関数はそのうちの一つである。 See section 組み込み関数に組み込み関数のリストと、関数の説明がある。 組み込み関数に加え、プログラムのどこかで使うためにユーザーが関数を定義することができる。 これについては、 See section ユーザー定義関数

関数を使うには、関数名とそれに続く括弧で囲まれた引数リストからなる 関数呼び出し(function call)式によって行う。 引数(arguments)は関数が計算を行う為に与えられる生のデータである。 二個以上の引数がある場合には引数の間にカンマを置く。 引数がない場合には `()'を関数名に続けて書く。

 
sqrt(x^2 + y^2)        一つの引数
atan2(y, x)            二つの引数
rand()                 引数なし

警告: 関数名と開き括弧の間には空白を置いてはいけない! ユーザー定義関数の名前は変数名と同じ様に見え、 空白は変数と括弧の中の式との連結のように見せてしまう。

With built-in functions, space before the parenthesis is harmless, but it is best not to get into the habit of using space to avoid mistakes with user-defined functions. Each function expects a particular number of arguments. For example, the sqrt function must be called with a single argument, the number of which to take the square root: 組み込み関数に対してカッコの前にスペースを置くことは害がないけれども、 ユーザー定義関数に関する間違いを避けるためにそういったことをしないのは 良いことである。

 
sqrt(argument)

一部の組み込み関数では最後の引数を省略することができ、 省略した場合には適当なデフォルトの値が使われる。 詳しくはSee section 組み込み関数。 ユーザー定義関数の呼び出しで引数を省略した場合、 そのような引数はローカル変数として扱われ、空文字列に初期化される (see section ユーザー定義関数)。

他の式と同様に関数呼び出しも値を持ち、 その値は与えた引数に応じて関数が計算した結果である。 例えば、`sqrt(argument)' の値は与えた引数(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
Ctrl
-d

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.14 演算子の優先順位 (How Operators Nest)

演算子優先順位(operator precedence)は一つの式の中で異なった演算子があったときに、 どのように演算子をまとめるかを決定する。 例えば、`*'`+'よりも優先順位が高く、 従って`a + b * c'bcを掛けて、 それに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)' であるということである。 これは、`-'の優先順位が`^'よりも低く、 `$'が全体で最も高い優先順位であるからである。 また、演算子は優先順位のルールに違反する場合結合することができない; 例 えば、`$$0++--' は最初の `$'`++' よりも高い優先順位 であるため妥当な表現である; 問題を取り除くには `$($0++)--' のよう に表現して書かれなければならない。

以下にawkの演算子を優先順位の高い順から並べた一覧を挙げる:

(…)

グルーピング。

$

フィールド。

++ --

インクリメントとデクリメント。

^ **

べき乗。これらの演算子は右結合する。

+ - !

単項のプラス、マイナス、論理否定。

* / %

乗算、除算、剰余。

+ -

加算と減算。

文字列連接

連接を指示するための特別なトークンはない。 オペランドを単純に並べて記述すればよい(see section 文字列連接)。

< <= == !=
> >= >> | |&

Relational and redirection. 関係演算子と、リダイレクション。これらは同じ優先順位である。 `>'の様なキャラクタは関係演算子としてもリダイレクションとしても使われる。 この両者の区別は文脈によって決定される。

print文やprintf文中の入出力リダイレクション演算子は 式ではなく文のレベルに属していることに注意すること。 リダイレクションは他の演算子のオペランドとなるようなな式を生成しない。 その結果、リダイレクションをより優先順位の低い演算子のそばにカッコなしで 記述するということは意味がなく、 例えば `print foo > a ? b : c'はシンタックスエラーとなる。 この文の正しい書き方は`print foo > (a ? b : c)'である。

~ !~

マッチング、非マッチング。

in

配列の要素。

&&

論理的な "and"。

||

論理的な "or"。

?:

条件。この演算子は右結合する。

= += -= *=
/= %= ^= **=

代入。これらの演算子は右結合する。

注意 : `|&', `**', `**='といった演算子はPOSIXでは規定されていない。 移植性を最大限に保つためにはこれらを使わないこと。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6. パターン、アクション、変数

既に説明したように、それぞれのawkの文はアクションを伴ったパターンから 構成されている。この章 ではパターンとアクションを どのように作成するかを説明する。

The pattern-action rules and the statements available for use within actions form the core of awk programming. In a sense, everything covered up to here has been the foundation that programs are built on top of. Now it's time to start building something useful. パターン-アクションルールとアクションの中で使うことのできる 文はawk言語の核である。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.1 パターンの要素

awkのパターンはルールの実行を制御する。 あるルールはそのパターンとカレント入力レコードがマッチしたときに実行される。 このセクションではパターンの記述の仕方を説明する。

/regular expression/

正規表現はパターンのようなものであり、 入力レコードのテキストに一致したときマッチする (See section 正規表現.)。

expression

一つの式。数値に変換して0でない、もしくは空でない文字列のときにマッチする (See section パターンとしての式.)。

pat1, pat2

カンマで区切られたパターンのペアで、レコードの範囲を指定する。 その範囲にはpat1にマッチする最初のレコードと pat2にマッチする最後のレコードの両方が含まれる (See section パターンを使ってレコードの範囲を指定する.)。

BEGIN
END

(See section 特殊パターン BEGINEND.) awkプログラムで、前処理もしくは後処理をするための特別なパターン (See section 特殊パターン BEGINEND.)

empty

空のパターンは全ての入力レコードにマッチする (See section 空パターン.)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.1.1 パターンとしての正規表現

これまでのサンプルの中で、正規表現をパターンとして使ってきた。 この種類のパターンは、あるルールのパターン部分にある単純な正規表現定数である。 それは`$0 ~ /pattern/' と同じ意味を持つ。 これは入力レコードが正規表現にマッチしたとき、 (入力レコードに)マッチするパターンである。例を挙げよう。

 
/foo|bar|baz/  { buzzwords++ }
END            { print buzzwords, "buzzwords seen" }

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.1.2 パターンとしての式

awkの任意の式は、そのまま正しいawkのパターンである。 そして、式の値が非0(数値の場合)でもなく、空でない文字列 (文字列の場合)の場合には、 パターンマッチするということになる。 式は、ルールが新たな入力レコードに対してテストされる度に再評価が行われる。 もしここで式が$1のようなフィールドを使っていたのならば、 その値は新たな入力レコードテキストによるものである。 それ以外のものはawkプログラムの実行時の動作にのみ依存する。

変数の型付けと比較式で説明されている比較演算子を使った 比較式は非常に一般的なパターンである。 正規表現のマッチングと否定マッチングもまた非常に一般的な式である。 `~'演算子や`!~'演算子の左オペランドは文字列である。 右オペランドはスラッシュで囲まれた(/regexp/)正規表現定数か 動的正規表現(see section 動的正規表現を使う)として使用される文字列値を持った 任意の式である。 以下の例は第一フィールドが`foo'であるようなレコードの 第二フィールドを出力する。

 
$ awk '$1 == "foo" { print $2 }' BBS-list

(これはなにも出力しない。なぜなら、`foo'という名前のBBSはないからだ。) 対照的に、次の例では第一フィールドに`foo'を含むレコードを出力するものである。

 
$ awk '$1 ~ /foo/ { print $2 }' BBS-list
-| 555-1234
-| 555-6699
-| 555-6480
-| 555-2127

パターンとしての正規表現定数もまた式パターンの特殊なケースである。 /foo/という式はカレントの入力レコード中に`foo'が 現れるかどうかの値を持っている。したがって、/foo/という パターンは`foo'を含む任意のレコードにマッチする。

ブール式もまた同様にパターンとして使われている。 パターンが入力レコードにマッチするかどうかを、 部分式がマッチするかどうかで判定する。 例を挙げると、次のコマンドは `2400'`foo'の両方を含む `BBS-list'中のレコード全てを出力する。

 
$ awk '/2400/ && /foo/' BBS-list
-| fooey        555-1234     2400/1200/300     B

次の例は`2400'`foo'のいずれか(両方でもよい)を含む `BBS-list'中のすべてのレコードをすべて出力する。

 
$ awk '/2400/ || /foo/' BBS-list
-| alpo-net     555-3412     2400/1200/300     A
-| bites        555-1675     2400/1200/300     A
-| fooey        555-1234     2400/1200/300     B
-| foot         555-6699     1200/300          B
-| macfoo       555-6480     1200/300          A
-| sdace        555-3430     2400/1200/300     A
-| sabafoo      555-2127     1200/300          C

次の例では、`foo'という文字列を含まない `BBS-list'のレコードを全て出力する。

 
$ awk '! /foo/' BBS-list
-| aardvark     555-5553     1200/300          B
-| alpo-net     555-3412     2400/1200/300     A
-| barfly       555-7685     1200/300          A
-| bites        555-1675     2400/1200/300     A
-| camelot      555-0542     300               C
-| core         555-2912     1200/300          C
-| sdace        555-3430     2400/1200/300     A

パターン中にあるブール演算子の部分式は、正規表現定数や、比較、あるいは任意の awk式であってよい。範囲パターンは式ではなく、 論理パターンの内側に置くことはできない。 同様に、あらゆる入力レコードとはマッチしないスペシャルパターン BEGINENDも式ではなく、論理パターンの内側に置くことはできない。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.1.3 パターンを使ってレコードの範囲を指定する

範囲パターン(range pattern)は`begpat, endpat'のように、 カンマで区切られた二つのパターンからなる。 これは連続した範囲の入力レコードにマッチする。 最初のパターンbegpatは範囲の始まりを制御し、 二番目のパターンendpatは範囲の終わりを制御する。たとえば

 
awk '$1 == "on", $1 == "off"' myfile

これは`myfife'の中で `on'/`off'にはさまれたレコードを `on'/`off'のあるレコードも含めて出力する。

範囲パターンは入力レコードと比較しbegpatとマッチすることで始まる。 レコードがbegpatとマッチしたとき、範囲パターンは真となる。 endpatとマッチする入力レコードがみつかるまで すべての入力レコードはパターンにマッチしたと扱われる。 endpatとマッチする入力レコードが見つかったとき、 範囲パターンはそれ以降の入力レコードのマッチングの際には偽となる。 そして今度はまた入力レコードとbegpatがマッチするかどうかを検査するのである。

範囲パターンを真にしたり、偽にしたりしたレコードそのものも 範囲パターンにマッチしたと扱われる。 もしここでそのようなレコードは扱いたくないというのであれば if文を使って、ルールのアクション部分でそのようなレコードを区別すれば良い。

範囲の始まりと終わりを同じパターンにすることも可能である。 そのような場合、アクションはマッチしたレコードに対してのみ実行される。 例えば、二つの目印(ここでは`%'としよう)に挟まれているテキストを 無視したいと考えたとしよう。 これを区切りのテキストを記述した範囲パターンとnext文 (まだ説明していない。セsee section next) を組み合わせて、 awkに処理をスキップさせることを 例えば以下のプログラムのようにして試みるかもしれない。

 
/^%$/,/^%$/    { next }
               { print }

このプログラムは、`%'だけがある最初の行で範囲の開始と終了を 行ってしまうので失敗してしまう。 望みどおりに動作させるには、フラグを使用して次のようなプログラムを書かなければならない。

 
/^%$/     { skip = ! skip; next }
skip == 1 { next } # skip lines with `skip' set

範囲パターンの中では、`,'はすべての演算子の中でもっとも優先順位の低い (最後に評価が行われる)演算子であることに注意すること。 したがって、次の例のようなプログラムでは別の単純なテストと範囲パターンが 組み合わされて扱われてしまう。

 
echo Yes | awk '/1/,/2/ || /Yes/'

このプログラムの作者は`(/1/,/2/) || /Yes/'のつもりだった。 しかし、awkはこれを`/1/, (/2/ || /Yes/)'のように解釈する。 これは変更したり、対処したりすることはできない。 範囲パターンは他のパターンと組み合わせないこと。

 
$ echo Yes | gawk '(/1/,/2/) || /Yes/'
error--> gawk: cmd. line:1: (/1/,/2/) || /Yes/
error--> gawk: cmd. line:1:           ^ parse error
error--> gawk: cmd. line:2: (/1/,/2/) || /Yes/
error--> gawk: cmd. line:2:                   ^ unexpected newline

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.1.4 特殊パターン BEGINEND

ここまで説明してきたパターンはすべて入力レコードとのマッチングが行われる。 BEGINENDは特殊なパターンである。 これらは入力レコードにマッチするためには使われず、 awkスクリプトのスタートアップやクリーンアップに使われる。 BEGINルールとENDルールはアクションを持たねばならない。 それらが実行されるときにはカレントレコードというものは存在しないので これらのルールはデフォルトアクションを持っていない。 BEGINルールとENDルールはベテランのawk プログラマからしばしば"BEGIN/ENDブロック"として 参照される。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.1.4.1 スタートアップとクリーンアップのアクション

BEGINルールは一度だけ、最初の入力レコードが読み込まれる前に実行され、 ENDもやはり一度だけ、すべての入力が行なわれた後で実行される。例を挙げよう。

 
$ awk '
> BEGIN { print "Analysis of \"foo\"" }
> /foo/ { ++n }
> END   { print "\"foo\" appears", n, "times." }' BBS-list
-| Analysis of "foo"
-| "foo" appears 4 times.

このプログラムは入力ファイル`BBS-list'中のレコードのうち、 `foo'という文字列を含むレコードの数を出力する。 BEGINルールでレポートのタイトルを出力する。 ここで、BEGINルールの中でカウンタに使用する変数 nを 0に初期化する必要はない。 awkが自動的にそれを行なうからである (see section 変数). 二番目のルールで、`foo'を含むレコードを読むたびに変数n をインクリメントしている。 ENDルールでは実行終了時のnの値を出力している。

特殊パターンBEGINENDは範囲式の中やブール演算子 と組み合わせて使うことはできない(そればかりかどの演算子とも組み合わせる ことができない)。 awkプログラムではBEGIN ルールや ENDルールを 複数記述することもできる。 そのような複数のルールは、プログラムの先頭から見付かった順に 全てのBEGINルールはスタートアップ時に、 全てのENDルールは終了時に実行される。 この機能は、awkの1987年のバージョンで付け加えられた。 また、これはPOSIXの標準にも含まれている。 オリジナル(1978年) のawkでは、 BEGINルールはプログラムの先頭に、 ENDルールはプログラムの最後に置かれていて、 それぞれプログラム中に一つだけあることを要求していた。 今やこうする必要はないのだが、このようにすることはプログラムの構成を改良し、 可読性を向上させるアイデアではある。

複数のBEGINENDはライブラリを記述するのに便利である。 ライブラリはそれぞれ自分のBEGIN ルールや ENDルールを 自分のスタートアップやクリーンアップのために持つことができる。 気を付けなければならないのは、 コマンドラインに記述されるライブラリの名前の順番によって、 (ライブラリ中の)BEGIN ルールや ENDルールの実行される順番が 左右されるということである。 したがって、ライブラリファイルで実行される順番に依存するような記述をしないように 注意しなければならない。 より詳しいライブラリ関数の使い方はSee section コマンドラインオプション、 便利なライブラリ関数はSee section awk 関数のライブラリ

BEGINルールだけで、他のルールを一切持っていないawkプログラムは、 BEGINルールを実行した後でプログラムの実行を終了する (27)。 しかし、ENDルールがあった場合には他のルールがなかったとしても、 入力の読み込みを行う。 これは、ENDルールで FNRNR という変数をチェックする場合に必要だからである。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.1.4.2 BEGIN/ENDルールでの入出力

BEGINルールやENDルールから入出力を行ったときに生じる幾つかの (時として微妙な)事柄がある。 第一の点は、BEGINルール中で$0の値を扱うことに関してのものである。 BEGINルールは何等かの入力が行われる前に実行されるので、 入力レコードも、フィールドもBEGINルール実行時には存在していない。 $0やフィールドを参照すると、 その結果は空文字列かゼロ(これは文脈による)のいずれかになる。 $0に本当の値を格納する手段としては、 変数指定なしでgetline(see section getlineを使った入力) コマンドを使うというものがある。 もう一つのやり方として、単に値を$0に代入するという手段もある。

第二の点は第一のものに似ているが違う方向のものである。 ENDルールの内側での$0NFは、 伝統的に大部分の処理系において未定義だった。 POSIXの標準は、 NFENDルール中で使用可能であることを規定しており、 その内容は最後の入力レコードのフィールド数を保持しているように定められている。 恐らくは見落としのために、この標準では論理上そう考えるべきであるにもかからわず、 $0を同様に保存するということに言及していない。 事実、 gawkENDルールのために$0の値を保存している。 しかしながら気をつけなければならないのは、Unixのawkや おそらくはその他の実装ではそうではないということである。

第三の点は、先の二つに関連するものである。 BEGINルールや ENDルール中で`print'が意味するところはなんだろうか? それは通常と同じく、`print $0'である。 $0が空文字列であれば空行が印刷される。 長い間awkプログラマは、BEGINルールやENDルールの中で $0が空文字列であることを当てにして `print ""'の意味で `print'を使っていた。 BEGINルールでこれを使っていたらそれはやめたほうが良い。 少なくともgawkを使っているときには、 これを ENDルールで使うのは悪いアイデアである。 これも出力において空行が必要なら良くないスタイルであり、 空の行を出力したいのならばそれを明確に記述すべきである。

最後に、next文とnextfile文はBEGINルールで使うことが 許されていない。なぜなら、暗黙のレコード読み込み-ルールとマッチングが まだ開始されていないからである。同様にこれらの文は ENDルールでも使うことができない。それはすべての入力は 読み込みが済んでしまっているからである (See section nextUsing gawk's nextfile Statement)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.1.5 空パターン

空のパターン(つまり、パターンがないということ)は すべての入力レコードとマッチするように扱われる。例えば次のプログラムでは、

 
awk '{ print $1 }' BBS-list

すべてのレコードの第一フィールドを出力する。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.2 プログラム中でシェル変数を使う

awkプログラムはしばしばシェルスクリプトで書かれた 大きなプログラムの一部として使われる。 たとえば、シェル変数をawkプログラムで探すための パターンを保持するものとして使うことは非常に一般的である。 シェル変数をawkプログラムに持ち込むにはふたつの やり方がある。

最も一般的なやり方はシェルのクォーティングを、 変数の値をスクリプトの中にあるプログラム中の値に置き換えるものとして 使うものである。 たとえば以下のプログラムでは:

 
echo -n "Enter search pattern: "
read pattern
awk "/$pattern/ "'{ nmatches++ }
     END { print nmatches, "found" }' /path/to/data

awkプログラムは、プログラムからは連結されて一つに見える ふたつのクォートされたテキストからなる。 最初の部分はダブルクォートで囲まれ、patternという変数は 置換が行われる。二番目の部分はシングルクォートで囲まれている。

クォーティングを通した変数置換は動作するけれども間違いの入り込む 可能性がある。そこではシェルのクォーティング規則についてよく知っておく 必要があり(see section シェルのクォーティングについて)、 プログラムを読むときに正しくクォーティングを読み解くことは しばしば難しいこととなる。

より良い方法は、awkの変数代入機構(see section コマンドライン上で変数に代入する) を、シェル変数の値をawkの変数の値に代入するのに 使うというものである。そして代入を行った後でパターンのマッチのために 動的正規表現(see section 動的正規表現を使う)を用いる。 以下の例は先の例をこのテクニックを使って書き直したものである。

 
echo -n "Enter search pattern: "
read pattern
awk -v pat="$pattern" '$0 ~ pat { nmatches++ }
       END { print nmatches, "found" }' /path/to/data

これで、awkプログラムはただひとつのシングルクォーティングされた 文字列となった。`-v pat="$pattern"'という代入はこの場合でも ダブルクォートで囲む必要があるけれども、これは$patternに 空白が含まれている場合に対処するためである。 awkの変数patpatternという名前にしても よいけれども、それは混乱を招きやすい。変数を使うことは より一層の融通性をもたらすことでもある。なぜなら、 変数はプログラムのどこでも クォーティングのトリックが要求されることなしに 使うことができるからである -- 出力のためとか、配列の添え字付けのため、あるいはその他のことのために。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.3 アクション

awkのプログラム(またはスクリプト)はルールと関数定義などの 並びの集まりである (関数はこの後で説明する。See section ユーザー定義関数)。 ルールはパターンとアクション、もしくはそのどちらか一方からなる。 アクションの目的はawkに対してパターンがマッチしたときに 何を行なうかを示すということである。 したがって、完全な形のawkプログラムは次のようなものである。

 
[pattern] [{ action }]
[pattern] [{ action }]
…
function name(args) { … }
…

アクションは、カーリーブレース(`{…}')に囲まれたひとつ以上の awkの文から構成される。 それぞれの文は一つのことをする。文は改行かセミコロンで区切られている。 たとえアクション部がたった一つの文だけであったり、 文がなかったとしても、アクション部をカーリーブレースで囲まなければならない。 しかし、アクション部を省略するのであればカーリーブレースも 一緒に省略することができる。 省略されたアクションは`{ print $0 }'と同じ働きをする。

 
/foo/  { }     fooにマッチしなにもしない --- 空のアクション
/foo/          fooにマッチしレコードを出力 --- 省略されたアクション

以下に挙げるものはawkがサポートする文の種類である。

関数呼び出しや変数への値の代入などの式 (see section )。 この種の文の実行は単に式の値を計算してそれを無視するだけである。 これは式の副作用を活用するということである (see section 代入式)。

制御文

awkプログラムの制御フローを特定する制御文。 awk言語ではCと同じような(if, for, while, などの) 制御文と、 幾つかの特殊な制御文がある(see section アクション中の制御文)。

複合文

一つ以上の文をカーリーブレースで囲んだ複合文。 複合文は幾つかの文を一つにまとめて、if文やwhile文、 do文、for文で扱うために使用する。

入力ストリーム

getlineコマンド(see section getlineを使った入力)で使用する。 同様にawkではnext文(see section next)や nextfile文(see section Using gawk's nextfile Statement)がある。

出力ストリーム

printprintf。 See section Printing Output

削除文

配列要素を削除するためのもの。 See section delete


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.4 アクション中の制御文

ifwhileなどの制御文(control statement)は、 awkプログラムの実行の流れを制御する。 awkの制御文のほとんどはCで使われているものと同じ形式である。

すべての制御文は単純な式と区別するために、 ifwhileなどのような特別なキーワードで始まる。 多くの制御文は他の文を構成要素に持っている。 例えばif文は実行されたりされなかったりする本体 と呼ばれるような別の文を持っている。 もし本体に二つ以上の文を含めたいのであればカーリーブレースを使うことにより、 セミコロンや改行で区切られた文の集まりを一つの複合文(compound statement) としてまとめることができる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.4.1 if-else

if-else文は意思決定をするawkの文であり、 次のような形をしている。

 
if (condition) then-body [else else-body]

conditionは文の残りの部分を制御する式である。 もしconditionが真であればthen-bodyが実行され、 偽であればelse-bodyが実行される。 else部分は省略可能である。 conditionはその値が0か空文字列であれば偽として見なされ、 それ以外の値は真であると見なされる。

 
if (x % 2 == 0)
    print "x is even"
else
    print "x is odd"

この例では、`x % 2 == 0'という式が真である (つまり、 xが 2で割り切れる値である)ときに最初のprint文を実行し、 そうでない(2で割り切れない)ときに二番目のprint文を実行する。 elsethen-bodyと同じ行にあり、 then-bodyが複合文でない (カーリーブレースで囲まれていない)とき、 セミコロンで then-bodyelseを区切らなければならない。 この規則により、先の例は次のように書ける。

 
if (x % 2 == 0) print "x is even"; else
        print "x is odd"

ここで`;'を付け忘れてしまった場合 awkは構文解析を続けられなくなり、 シンタックスエラーとなる。 我々は実際にはこの例のような書き方はしない。なぜなら人が読むときに、 行の最初になければelseを読み落す可能性があるからである。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.4.2 while

プログラミングにおいては、ループ(loop)とはプログラムの一部分を二回以上実行する (少なくともその可能性がある)ことである。 while文はawkの中で最も単純な繰り返しの文である。 条件が真である間、文を繰り返し実行する。 while文は次のような形をしている。

 
while (condition)
  body

ここでbodyは我々が繰り返しの本体と呼んでいる文であり、 conditionはどれ位の間繰り返しを続けるかを制御する式である。 while文で最初に行なわれるのはconditionの検査である。 もし conditionが真であればbodyの文を実行する。 bodyを実行したあとで再び conditionをテストし、 それがまだ真であればbodyが実行される 。 このプロセスはconditionが真でなくなるまで繰りかえされる。 condition が最初に偽であった場合ループの本体は決して実行されず、 awkはループに続く文に移る。 次の例は、入力行の最初の三つのフィールドを一行にフィールドひとつという形で出力する。

 
awk '{ i = 1
       while (i <= 3) {
           print $i
           i++
       }
}' inventory-shipped

ここでループの本体は二つの文をカーリーブレースでくくった複合文である。 このループは次のように動作する。最初にiの値を1にセットし、 その後に whileiが3以下であるかどうかを検査する。 iが3以下であったときi番目のフィールドが出力される。 `i++'によってi の値が増やされてループが繰り返される。 ループはiが4になったときに終了する。

例を見るとわかる通り改行は条件と本体の間になくても良い。 しかし本体が複合文か非常に単純なものでない限り、 改行を入れることによってプログラムの見通しがよくなる。 複合文の初めにある左カーリーブレースのあとの改行はなくても構わないが、 プログラムが読み辛いものになるだろう。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.4.3 do-while

doループはwhile文のバリエーションである。 doループでは bodyが一回実行され、 その後conditionが真である間bodyが繰り返される。 doループは次のような形である。

 
do
  body
while (condition)

開始時にconditionが偽であったとしても、 bodyが少なくとも一回 (bodyの実行によってconditionが真にならない限りはそれきり)は実行される。 対応するwhile文とは対照的である。

 
while (condition)
  body

この文ではconditionが開始時に偽であった場合、bodyは実行されない。 do文の例を挙げよう。

 
{      i = 1
       do {
          print $0
          i++
       } while (i <= 10)
}

この例ではレコードが入力されるごとに10回出力を行なう。 これは通常はwhile で行なうようなものであるから現実的な例ではない。 しかし、それは実際の状況を反映している。 なぜなら本当にdo文を使うことというのはまれであるからである。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.4.4 for

for文はループの繰り返しを数えるのに便利である。 一般的なfor文は次のような形をしている。

 
for (initialization; condition; increment)
  body

initialization, condition, increment といった部分はawkの式を置くことができ、 bodyawkの文を表わしている。

for文はinitializationの実行で始まり、 続いてcondition が真である間繰り返してbodyincrementが実行される。 典型的な例では、initializationで変数に0や1をセットするということが行なわれ、 incrementでその変数に1加えられる。 そしてcondition ではそれが繰り返しの回数を決める数と比較される。

 
awk '{ for (i = 1; i <= 3; i++)
          print $i
}' inventory-shipped

この例では、入力レコードごとに最初の三つのフィールドを一行にひとつのフィールドで出力する。

initialization部分に`x = y = 0'のような多重代入文を使わずに 二つ以上の変数を置くことはできない。このことはすべての初期値が同じ値で なければならないということである(forループの前に独立して代入文を 置くことによって複数の変数の初期化を行うことは可能である)。

このことは変数のインクリメントを行なうincrement部でも同じことである。 もしインクリメントする文を追加したいのなら、 ループの終わりに文を分けて書かなければならない。 Cのカンマ演算子を使ったCの複合式は、そのような文脈であっても使うことができる。 が、awkではサポートされていない。

ほとんどの場合、incrementは先に挙げた例のように増減式(increment expression)である。 しかし、常にそうである訳ではない。 式であれば何でも記述することができる。 たとえば次の例では、2の1乗から100乗まで出力する。

 
for (i = 1; i <= 100; i *= 2)
  print i

forに続くカッコの中にある三つの式はいずれも必要がなければ省略することができる。 したがって、`for (;x > 0;)'`while (x > 0)'と等価である。 もし、conditionが省略されたならばそれはtrueが常に真であると解釈され、 無限ループ (infinite loop, 終了せずに繰り返しを続けるループ)となる。

forループの大部分は、次のようなwhileループの省略形である。

 
initialization
while (condition) {
  body
  increment
}

唯一の例外は、ループの中で continue文 (see section continue) が使われているときである。 このやり方でfor 文を while文に書き換えるときは ループの中のcontinue文の効果を変える可能性がある。

awk言語は、forループがしばしばタイプ量が少なくて より自然な考え方であることからwhile文に加えてforも持っている。 繰り返しの回数を数えるのは非常に一般的なループである。 ループの中で数え上げを行うよりも数え上げを行う部分を持っていたほうが より自然であると考えられる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.4.5 switch

注意 : この サブセクション では、gawk 3.1.3 で追加された 実験的な機能について説明している。これはデフォルトでは有効になっていない。 有効にするためには、gawkの configure 時に `--enable-switch' オプションを configure に与える。 詳細については See section 付加的なコンフィグレーションオプション.を参照のこと。

switch 文は式の評価を許し、そしてマッチしたcaseに基づいて 文を実行する。case 文はそれが定義された順にマッチが試される。適当な case が見つからなければ、(もしあれば)fefault セクションが実行される。

case 式は評価され、その後でそれぞれの case の定数が 評価結果と比較される。定数の型は比較を決定する: 数値もしくは文字列であれば 通常の式と同じ文字列比較が行われる。正規表現定数は元の式の文字列値に 対して正規表現式のマッチングが行われる。 一般的な形式の switch 文は以下のようなものである:

 
switch (expression) {
case value or regular expression:
    case-body
default:
    default-body
}

switch文はの制御フローは C と同じように動作する。与えられた caseとマッチしたならば、 case文の本体はbreakcontinuenextnextfileexit に出会うか、switch 文本体の終端まで実行する。たとえば:

 
switch (NR * 2 + 1) {
case 3:
case "11":
    print NR - 1
    break

case /2[[:digit:]]+/:
    print NR

default:
    print NR + 1

case -1:
    print NR * -1
}

マッチしたcaseの実行を停止する文が指定されていない限り、 次のcase文にフォールスルーすることに注意すること。上にあげた例では、 `2'で始まりそれに後続する一文字以上の数字はprint文を実行し、 default セクションにフォールスルーして、そこのprint文を 実行する。そして-1 の場合も実行を停止する文がないのでdefault を実行する。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.4.6 break

break文は最も内側のforwhile、または doのループから脱出する。 以下に挙げる例はある整数の最も小さい約数を探し、素数であるか確認する。

 
# num の最も小さい約数を探す
{
   num = $1
   for (div = 2; div*div <= num; div++)
     if (num % div == 0)
       break
   if (num % div == 0)
     printf "Smallest divisor of %d is %d\n", num, div
   else
     printf "%d is prime\n", num
}

最初のif文で剰余が0であったとき、awkは即座に forループからbreaks outする。 これはawkはループに続く文に即座に進み、処理を続けるということである (これはexit文がawkプログラムの実行を完全に止めてしまう ということとは異なっている。See section exit)。

次の例は先の例と等価な別のプログラムである。 これはforwhile の条件がifの内側にある break に置き換えることができるということを示している。

 
# num の最も小さい約数を探す
{
  num = $1
  for (div = 2; ; div++) {
    if (num % div == 0) {
      printf "Smallest divisor of %d is %d\n", num, div
      break
    }
    if (div*div > num) {
      printf "%d is prime\n", num
      break
    }
  }
}

break文をループ本体の外側で使うことは意味がない。 しかしながらこれはドキュメント化されてはいないのだが、 awkの伝統的な実装においては、 ループの外側でbreakが使われた場合にはそこに next 文が置かれていたのように動作する (see section next)。 最近のUNIX上のawkではこのような使い方は許されていない。 gawk`--traditional' がコマンドラインオプションで指定されたときのみ (see section コマンドラインオプション)、この動作をする。 指定がなかった場合には、 POSIX標準ではbreakはループ本体内でのみ使うことが規定されているので、 エラー扱いとなる。 (d.c.)


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.4.7 continue

continue 文は breakと似ていて、 forwhiledoのループの中でだけ使われる。 continueはループ本体の残りの部分をスキップし、 ループの次のサイクルを即座に始めさせる。 これはbreakの動作、ループの外側にジャンプするというのとはまったく対照的である。

forループ中のcontinue文は awkに直接ループ本体の残りの部分をスキップさせ、 for文の increment-expressionを実行させる。例を挙げよう。

 
BEGIN {
     for (x = 0; x <= 20; x++) {
         if (x == 5)
             continue
         printf "%d ", x
     }
     print ""
}

このプログラムは0から20のうち、5を除いたすべての数を出力する。 `x++'がスキップされないので、xは5のまま止まるということはない。 この forループは次に挙げるwhileループとは対照的である。

 
BEGIN {
     x = 0
     while (x <= 20) {
         if (x == 5)
             continue
         printf "%d ", x
         x++
     }
     print ""
}

このプログラムはxが一度5になると、永久にループし続ける。

continue文はループの外側で使っても何の意味もない。 しかしドキュメント化されてはいないが、伝統的なawkの実装では、 ループの外側にあるcontinue文をnext文であるかのように扱う (see section next)。 最近 のUNIXのawkはこの使い方をもはやサポートしていない。 gawk`--traditional'オプションが コマンドラインで指定された場合にこれをサポートする。 それ以外の場合はエラーとする。 なぜなら POSIX 標準では continueはループ本体の内側で使わなければならないとされているからである。 (d.c.)


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.4.8 next

next文は強制的にawkに対して、 即座にカレントレコードの処理を中断させ、次のレコードの処理に移させる。 つまり、その先のルールはカレントレコードに対して適用されないということである。 同様に、その時点で適用されているルールのアクション部の残りもまた実行されない。

これと対照的な効果をもたらすのがgetline関数 (see section getlineを使った入力)である。 getlineawkに次のレコードを即座に読み込みさせるが、 その後の制御は変更しない。したがって、 その時点で実行しているアクションの残りの部分は、新しい入力レコードに対して行なわれる。

最も高いレベルでは、awkプログラムの実行とは入力レコードを読みこんで それに対してそれぞれのルールのパターンを突きあわせるというループである。 このループを本体にルールがある forループのようなものだと考えるならば、 next文は continue文のようなものである。 この暗黙のループの本体の最後までスキップし、 increment(つまり、別のレコードを読む)を実行する。

例えば、あなたのawkプログラムが4つのフィールドからなるレコードに対してだけ働き、 それ以外の入力が与えられたときに失敗したくない。 というのであれば、プログラムの先頭近くのルールで次のように書けば良い。

 
NF != 4 {
  err = sprintf("%s:%d: skipped: NF != 4\n", FILENAME, FNR)
  print err > "/dev/stderr"
  next
}

next文があるので、このプログラムは不正なレコードを 後続のルールで検査することはない。 エラーメッセージはそうであるべきように 標準エラー出力にリダイレクションされる。 詳しくはgawk における 特殊な ファイル名

POSIXの標準によると、next文がBEGINルールやENDルールにあるときの 動作は未定義である。gawkはこれをシンタックスエラーであるとみなす。 POSIXでは許されていても、一部のawk処理系では nextが関数本体の内側に置かれることを許していなかった (see section ユーザー定義関数)。他のnext文の使い方と同じように、 関数本体の内側にあるnext文は、次のレコードを読み込み、 プログラムの最初のルールから処理を開始する。 next文の実行により入力の終端に達した場合にはENDルールにある コードが実行される。See section 特殊パターン BEGINEND


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.4.9 Using gawk's nextfile Statement

gawknext文に似たnextfile文を提供している。 これはnextが行っているようなカレントレコードの処理を放棄するものではなく、 gawkに対してカレントデータファイル の処理をやめさせるものであ る。

nextfile文はgawkでの拡張機能である。 この機能は(少なくとも現在のところ)他のawk処理系や gawkが互換モード(see section コマンドラインオプション)のときは使用できない。

nextfile文の実行によって、FILENAMEは コマンドラインでリストアップされたものの中で 次のデータファイル の名前に更新される。同時に、 FNRは1にリセットされ、ARGINDはインクリメントされ、 プログラムの最初のルールから処理が始められる (ARGINDはまだ説明していない。See section 組み込み変数)。 nextfile文の実行により入力の終端に達したならば、 ENDルールにあるコードが実行される。 See section 特殊パターン BEGINEND

nextfile文は多くのデータファイルを処理する状況で、 データの性質によって、ファイル中のレコードの全てを処理するということを 望まない場合に便利である。 通常は次のデータファイルに移る手段として、 希望しないレコードをスキャンし続けなければならないだろう(先程書いたように)。 nextfile文はより能率的にそれを実行する。

`close(FILENAME)'nextfileと同じ効果をもつと考える人が 中に入るかもしれないが、それは正しくない。 closeはリダイレクションによってオープンされた ファイル、パイプ、子プロセスをクローズするためにある。 ARGVに格納されているawkが処理するファイルとは関係がない。

nextfileをサポートしていないawkを使う必要があるのなら ユーザー定義関数によりnextfileをシミュレートしている nextfile の関数としての実装を参照のこと。

現在のバージョンのベル研究所によるawk(see section 他の自由に入手することのできる awk 処理系) もまたnextfileをサポートしている。 しかしながら、関数の本体(see section ユーザー定義関数)でnextfileを使うことはできない。 gawkはできる。関数本体にあるnextfileは その他の場所のnextfileがそうであるように 次のレコードを読み込みプログラムの最初のルールから処理を開始する。

警告: 3.0以前のバージョンのgawkでは、 nextfile文には二つの単語(`next file')を使っていた。 これは3.0で一つの単語に変更された。 それは、 `file'の取り扱いが一貫性のないものであったからだ。 `file'がnextの後に現れた場合それはキーワードとなる。 しかしそれ以外の場合には通常の識別子になるのだ。 古い使用法はもはや受け付けられない。`next file'は 構文エラーとなる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.4.10 exit

exit文はawk]に対して即座に現在処理しているルールの実行をやめさせ、 入力の処理もやめさせる。残った入力は無視される。 exit文は以下のように記述する

 
exit [return code]
 

exit文がBEGINルールで実行された場合プログラムは停止し、 すべての処理も即座に停止して、入力レコードは読まれない。 しかし、ENDルールがある場合にはそれが実行される (see section 特殊パターン BEGINEND)。 exitENDルールで使われた場合にはプログラムは即座に停止する。

BEGIN ルールでも ENDルールでもないところで exit文を使った場合、 それ以後のルールの実行は行なわれないが、 ENDルールだけは(記述されていれば)実行される。

もし、このような場合にENDルールを実行したくないというのであれば、 exit]文の前で、ある変数に0以外の値をセットし、 その変数を ENDルールの中でチェックすれば良い。 この例はSee section 表明(Assertion)

exitに引数を与えた場合、 その値はawkを実行しているプロセスの終了ステータスの値として使われる。 引数がない場合には、exitは0(成功)を返す。 引数つきのexit文を最初に実行してその後で引数なしの exit文を実行すると、 その前に使った引数が終了ステータスとして使われる。 (d.c.)

例えば対処できないエラー状態になったときにそれを報告するとしよう。 一般的にはプログラムはこれを0以外の終了コードによって報告する。 awkプログラムはこれを、 exit文を0以外の引数付きで使うことによって実現できる。 例を挙げよう。

 
BEGIN {
       if (("date" | getline date_now) <= 0) {
         print "Can't get system date" > "/dev/stderr"
         exit 1
       }
       print "current date is", date_now
       close("date")
}
  

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.5 組み込み変数

大部分のawkの変数はユーザが好きに使うことができる。 そういった変数は自分で代入をしない限り値が変わるようなことはないし、 プログラムに対して何か影響を与えるということもない。 しかし、awkの一部の変数は特別な組込みの意味を持っている。 その変数の一部はawkが自動的にその値を評価する。 そのような変数を操作することによって awkプログラムの動作を操作することが可能である。 他の変数はawkがその値を自動的にセットする。 それを参照することによってプログラム中でawk がどのように働いているのかについて情報を得ることができる。

このセクション では、gawkのすべての組み込み変数を 説明している。それらの大部分はそれが影響を及ぼすエリアについての 章でも説明されている。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.5.1 awkを制御する組み込み変数

以下に挙げるリストはアルファベット順に並べられた、awkを制御する ために変更することのできる変数である。gawkに固有の変数は ポンドサイン (`#')がついている。

BINMODE #

非POSIXのシステム上で、この変数はすべての入出力においてバイナリモードを 使うことを指定する。数値の1, 2, 3はそれぞれ入力ファイル、出力ファイル すべてのファイルでバイナリモードでの操作を指示する。 それとは別に、文字列での"r""w"はそれぞれ 入力と出力をバイナリモードで行うことを指示する。 "rw""wr"という文字列はすべてのファイル操作を バイナリモードで行うよう指示する。 その他の文字列は"rw"と等価であるが、gawkは 警告メッセージを出力する。 BINMODEPC オペレーティングシステム上での gawk の使用法に詳細がある。

この変数はgawkの拡張である。他のawk処理系(mawkを 除く see section 他の自由に入手することのできる awk 処理系)や互換モード(see section コマンドラインオプション)では特別ではない。

CONVFMT

この変数に格納されている文字列は数値から文字列に変換 (see section 文字列と数値の変換) する際にawkが使用する (see section 文字列操作関数)。 この変数は参照されたときに、sprintf関数の第一引数であるような効果を持つ。 デフォルトでは"%.6g"という値になっている。 CONVFMTは POSIX の標準によって導入された。

FIELDWIDTHS #

これは gawkに対して固定したカラム境界で入力を扱うように指示するカラムのリストで、 リストの個々の要素はスペースで区切られている。 FIELDWIDTHSに対する代入はフィールド分割の為のFSの使用をオーバーライドする。 より詳しくは、See section 固定長データを読む

互換モードでgawkが起動された場合 (see section コマンドラインオプション) FIELDWIDTHSは特別な意味を持たず、 フィールド分割の処理はFSの値によってのみ行われる。

FS

FSは入力フィールドセパレータである (see section フィールドがどのように分割されるかを特定する)。 その値は一文字の文字列、もしくは複数の文字で構成される正規表現で、 入力レコードをフィールドに分割するときの分割部分にマッチする。 FSに空文字列("")をセットすると、 レコード中のキャラクタ一文字をフィールド一つとして分割を行う。

デフォルトの値は" "で、一つのスペースからなる文字列である。 例外として、この値は連続しているスペース、タブ、改行を一つのセパレータとして認識させ (28)、 行の先頭や終端にあるスペース、タブの連続を無視させる。

`-F'オプションを使うことにより、 コマンドライン上で FSの値を変更することが出来る。

 
awk -F, 'program' input-files
  

gawkがフィールド分割をFIELDWIDTHSを使用して行っているときに FSに値を代入すると、 gawkは通常の正規表現によるフィールド分割を行う状態に戻る。 これはただ単純に`FS = FS'としても同じ結果となる。

IGNORECASE #

IGNORECASEが非0であるとき、全ての文字列比較は大小文字を無視して行われる。 このため、 `~'`!~'を使った正規表現のマッチング、 gensub, gsub, index, match, split, sub といった関数の正規表現マッチング、RSによるレコードの区切り、 FSを使ったフィールド分割は全て大小文字を無視して行われる。 ただし、IGNORECASE の値は配列の添え字付けには何の影響も及ぼさない。 See section Case Sensitivity in Matching

gawkが互換モード (see section コマンドラインオプション)で動作しているときは、 IGNORECASE は特別な意味を持たず、 正規表現は常に大小文字を区別してマッチングが行われる。

LINT #

この変数が真(つまりゼロでも空文字列でもない)とき、gawkは コマンドラインオプション `--lint' が指定されたときのように振舞う (see section コマンドラインオプション)。 値が"fatal"であったとき、lint警告は致命的エラーとなる。 値が"invalid"であったときには不正とみなされることについてのみ 警告が発せられる(これはまだ完全には実装されていない)。 その他の値は致命的でない警告を出力する。 LINTに偽の値を代入することはlint警告をオフにする。

この変数はgawkの拡張である。 他の実装のawkでは特殊ではない。 他の特殊変数とは異なり、gawkが互換モードで動作している場合でも LINTの値を変更することはlint警告の生成に影響を及ぼす。 `--lint'`--traditional'が独立にgawkの 動作を制御するように、lint警告のプログラム実行中の制御は awkが実行されている環境とは独立している。

OFMT

この文字列はawkが print文で出力を行うときに、 数値を文字列に変換するときの動作を制御するのに使用される (see section 文字列と数値の変換) 。 この変数は参照されたときに、 sprintf (see section 文字列操作関数) の第一引数であるような効果を持つ。 デフォルトでは"%.6g"という値になっている。 初期の awkではOFMTを、数値から文字列に変更するような全ての式で 書式を特定するために使っていたが、 その役割は現在ではCONVFMTが果たしている。

OFS

出力フィールドのセパレータ (see section 出力セパレータ)で、 print文を使ったときにフィールドとフィールドの間に出力される。 デフォルトは" "(一つのスペースからなる文字列)である。

ORS

出力レコードのセパレータ。 print文で出力したときに最後に(付加されて)出力される。 デフォルトは改行コード一つからなる文字列である("\n"という文字列)。 (see section 出力セパレータ)

RS

これはawkに対する入力のレコードセパレータである。 デフォルト値は改行キャラクタである。 つまり、デフォルトでは入力レコードはテキスト中の一行である。 これは空文字列や正規表現であってもよく、 空文字列の場合はレコードは空行の連続によって区切られ、 正規表現の場合にはその正規表現にマッチした入力テキストによって区切られる。 (See section 入力がどのようにレコードに分割されるか)。

RSが正規表現にできるのはgawkの拡張である。 他のほとんどのawkの実装(や互換モード(see section コマンドラインオプション)での gawk)では、RSの最初のキャラクタだけが使われる。

SUBSEP

SUBSEPは添字を区切るものであり、そのデフォルトの値は"\034"である。 この値は多次元配列の名前を分割するために使われる。 したがって foo["A","B"]と記述すると、実際はfoo["A\034B"] にアクセスする (see section 多次元配列)。

TEXTDOMAIN #

The default value of TEXTDOMAIN is "messages". この変数はawkレベルでのプログラムの国際化に使用される。 この変数にはマーク付けされたソーステキスト中にある文字列定数ための デフォルトのテキストドメインを dcgettextdcngettextbindtextdomain関数のために セットする(see section gawkの国際化)。

この変数はgawkの拡張である。 他の実装のawk(や互換モード(see section コマンドラインオプション)でのgawk)では 特殊なものではない。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.5.2 情報を伝達する組み込み変数

以下のリストはawkによって自動的に値がセットされ、 プログラム中で情報を得ることのできる変数のリストである。 gawkに特有の変数には`#'が目印についている。

ARGC, ARGV

awkプログラムに対するコマンドライン引数は、 ARGVと呼ばれる配列に格納される。 ARGCにはコマンドライン引数の数が格納される。 See section その他のコマンドライン引数. 大部分のawkの変数とは異なり、 ARGVの添え字付けは 0からARGC - 1である。

 
$ awk 'BEGIN {
>         for (i = 0; i < ARGC; i++)
>             print ARGV[i]
>      }' inventory-shipped BBS-list
-| awk
-| inventory-shipped
-| BBS-list
  

この例では、ARGV[0]には"awk"が、 ARGV[1]には "inventory-shipped"が、 ARGV[2]には"BBS-list"が入っている。 ARGVの値は3であり、それはARGVの最大のインデックスよりも一つ大きい。 それは、インデックスを0から数えるからである。

ARGCARGVの名前と、その添え字付けがゼロからARGC - 1 であるということはC言語で使われているコマンドライン引数の取り扱い方法からきている。

ARGV[0]の値はシステムによって異なる可能性がある。 同様に、プログラムテキストとawkのコマンドラインオプションが ARGV含まれていないことに注意すべきである。 awkがこれらの変数をどのように使うかはSee section ARGCARGV を使うを参照。

ARGIND #

現在処理を行っているファイルのAEGVにおけるインデックス。 gawkは新しいデータファイル をオープンする度に、 AERGINDに処理するファイル名のあるARGVのインデックスをセットする。 awkがファイルを処理している間は、 `FILENAME == ARGV[ARGIND]'は常に真である。

この変数はファイル処理に便利である。 データファイル のリスト中でリスト中のどこのファイルであるかを知ることができるし、 コマンドライン上で同じ名前で指定されている複数のインスタンスを区別することができる。

awkプログラム中でARGINDの値を変更することができ、 それにより gawkは自動的に次のファイルをオープンするときに新しい値をセットする。

この変数はgawkの拡張である。 ほかのawkや、gawkが互換モード (see section コマンドラインオプション) で動作している場合は特殊変数ではない。

ENVIRON

環境変数の値で構成される連想配列である。配列は環境変数の名前で添字付けされる。 要素の値は各環境変数の値である。 例えば、 ENVIRON["HOME"]の値は`/home/arnold/'のようである。 この配列の要素を変更しても awkがリダイレクションや system関数で起動する他のプログラムに渡される環境変数そのものには影響しない

一部のオペレーティングシステムでは環境変数を備えていないものがある。 このようなシステムの場合、配列ENVIRONは空である (ENVIRON["AWKPATH"]を除く。see section AWKPATH 環境変数)。

ERRNO #

getlineのリダイレクト、getlineでの読み込み、 closeでシステムエラーが発生したときに ERRNOにはエラーを表わす文字列がセットされる。

ERRNOは Cの変数errnoと同様の働きをする。 gawkはそれをクリアすることはない (ゼロや""をセットすること)。 したがって、入出力の操作が失敗したとき、たとえばgetlineが -1を返したときにのみ期待した値があると考えるべきである。 もちろん、ある入出力操作をする前に自分でクリアすることは自由にできる。

この変数はgawkの拡張である。 ほかのawkや、 gawkが互換モード (see section コマンドラインオプション) であるときは特殊変数ではない。

FILENAME

この変数にはawkが現在読み込んでいるファイルのファイル名がセットされている。 awkが標準入力から入力を行っている場合 (言い替えればコマンドラインでファイル名が与えられていない場合) には FILENAME"-"がセットされる。 FILENAME は新しくファイルが読み込まれる度に変更される (see section 入力ファイルを読む). BEGINルールの中では入力ファイルがまだ処理されていないので、 FILENAMEの値は""である。(29) (d.c.) しかし、BEGINルールでgetlineを使った場合には FILENAMEには値があることに注意すること。

FNR

FNRには、現在処理しているファイルでのカレントレコードの番号がセットされている。 FNRはレコードの入力の度に更新される (see section getlineを使った入力)。 入力ファイルが新しくなる度に0に初期化される。

NF

NFにはカレントレコード中のフィールドの数が格納されている。 NF はレコードの入力が行われて新しいフィールドが作られたとき、 もしくは$0 が変更される度に更新される (see section フィールドを検査する)。

この セクション で説明されているほとんどの変数とは異なり、 NFに対して値を代入することはawkの内部動作に影響を及ぼす可能性がある。 NFに値を代入することをカレントレコードにおいてフィールドを削除したり 生成したりすることに使うことができる。 See section フィールドの内容を変更する

NR

この変数にはプログラムが実行開始してからそれまでに awkが処理したレコードの数が格納されている (see section 入力がどのようにレコードに分割されるか)。 NR は新しいレコードが入力される度に更新される。

PROCINFO #

この配列の要素は実行中のawkプログラムに関する情報に アクセスする手段を提供する。以下の要素が使用可能である (アルファベット順)。

PROCINFO["egid"]

getegidシステムコールの値。

PROCINFO["euid"]

geteuidシステムコールの値。

PROCINFO["FS"]

FSがフィールド分割で使われているときには"FS"が、 FIELDWIDTHSが使われているときには"FIELDWIDTHS"がその値となる。

PROCINFO["gid"]

getgidシステムコールの値。

PROCINFO["pgrpid"]

カレントプロセスのグループID。

PROCINFO["pid"]

カレントプロセスのプロセスID。

PROCINFO["ppid"]

カレントプロセスの親プロセスID。

PROCINFO["uid"]

getuidシステムコールの値。

PROCINFO["version"]

gawkのバージョン。 これは3.1.4以降で使用可能である。

一部のシステムでは、幾つかのNについて "group1"から"groupN"までが配列要素に存在する。 Nはそのプロセスが持っているsupplementary グループの数である。 これらの要素を確認するのにはin演算子を使う (see section 配列要素への参照)。

この配列はgawkの拡張である。 ほかのawkや、 gawkが互換モード (see section コマンドラインオプション) であるときは特殊変数ではない。

RLENGTH

RLENGTHにはmatch関数でマッチした部分文字列の長さが格納されている (see section 文字列操作関数)。 RSTARTmatch関数を起動すると値がセットされる。 マッチする部分文字列があればその部分文字列の長さが、 ない場合には-1が格納される。

RSTART

RSTARTmatch 関数でマッチした部分文字列の最初のキャラクタのインデックスが入る (see section 文字列操作関数)。 RSTARTmatch関数を起動するとセットされ、 マッチする部分文字列があればその開始位置が、ない場合には0が格納される。

RT #

これはレコードが読み込まれるたびにセットされる。その内容は レコードセパレータ RS で表されるテキストにマッチした入力テキスト である。

この変数は gawk の拡張である。他の awk処理系や gawk の互換モード(see section コマンドラインオプション) では特別ではない。

Advanced Notes: Changing NR and FNR

awkはレコードを読み込む度に読み込んだレコードの数そのものをこれらの変数に セットするのではなく、これらの変数を単純にインクリメントする。 これはあなたがプログラム中でこれらの変数の値を書き換えることができるということであり、 それ以後はレコードを読み込む度にその値をインクリメントしていくのである。 (d.c.) 以下に挙げたのはその実例である。

 
$ echo '1
> 2
> 3
> 4' | awk 'NR == 2 { NR = 17 }
> { print NR }'
-| 1
-| 17
-| 18
-| 19
  

FNRawk言語に追加される以前 (see section Major Changes Between V7 and SVR3.1)には、 多くのawkプログラムでファイルごとのレコード数を記憶するのに、 FILENAMEが代わる度にNRを0にリセットするということでこの仕様を利用していた。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.5.3 ARGCARGV を使う

情報を伝達する組み込み変数では、 ARGCARGVに格納されている情報に関する説明を行っている。

 
$ awk 'BEGIN {
>        for (i = 0; i < ARGC; i++)
>            print ARGV[i]
>      }' inventory-shipped BBS-list
-| awk
-| inventory-shipped
-| BBS-list
  

この例では、ARGV[0]には`awk'が、 ARGV[1]には `inventory-shipped'が、 そしてARGV[2]には `BBS-list'がセットされている。 awkプログラムはARGVには含まれていないことに注意。 その他の引数を伴った特殊なコマンドラインオプションも含まれていない。 しかし、コマンドライン上での変数代入は引数として扱われる。 これには`-v'オプションによって代入された変数も含まれる (see section コマンドラインオプション)。 コマンドライン上にある通常の変数代入は引数として扱われARGV配列に含まれている

 
$ cat showargs.awk
-| BEGIN {
-|     printf "A=%d, B=%d\n", A, B
-|     for (i = 0; i < ARGC; i++)
-|         printf "\tARGV[%d] = %s\n", i, ARGV[i]
-| }
-| END   { printf "A=%d, B=%d\n", A, B }
$ awk -v A=1 -f showargs.awk B=2 /dev/null
-| A=1, B=0
-|        ARGV[0] = awk
-|        ARGV[1] = B=2
-|        ARGV[2] = /dev/null
-| A=1, B=2
  

プログラム中でARGCARGVを変更することができる。 awkは入力ファイルの終端に達する度に、 ARGVの次の要素を次の入力ファイルのファイル名として扱う。 そこに異なる文字列を格納することによって読み込むファイルを変更することができる。 このとき、標準入力は"-"で表わすことができる。 新しい要素を追加してさらにARGCをインクリメントすることによって、 読み込むファイルを増やすことができる。

ARGCの値をデクリメントすると、リストの最後にある入力ファイルがリストから削除される。 ARGCの古い値を他の場所で使うことにより、 リストから削除された引数をファイル名以外のものとして使うことができるようになる。

リストの中程にあるファイルをリストから削除するには、 空文字列("")を対象となるファイルの名前が格納されている ARGVの要素に代入してしまえば良い。 特殊な仕様として、awkは空文字列に置き換えられたファイル名を無視する。 同様に、ARGVからある要素を削除するためにdelete 文を使うこともできる (see section delete)。

これらのアクションは、典型的には入力に対して何等かの処理が行われる前、 BEGINルール中で行われる。 See section Splitting a Large File into Pieces。 また、ARGVから要素を削除するやり方は Duplicating Output into Multiple Files。 以下のコード片はARGVを順番に確認し、 そこからコマンドラインオプションを削除する処理をしている。

 
BEGIN {
    for (i = 1; i < ARGC; i++) {
        if (ARGV[i] == "-v")
            verbose = 1
        else if (ARGV[i] == "-d")
            debug = 1
        else if (ARGV[i] ~ /^-./) {
            e = sprintf("%s: unrecognized option -- %c",
                    ARGV[0], substr(ARGV[i], 2, 1))
            print e > "/dev/stderr"
        } else
            break
        delete ARGV[i]
    }
}
  

awkプログラムにオプションを渡すために、 awkに対するオプションの末尾に`--'を与えて、 その後にawkプログラムに対するオプションを、 以下に示す例のようにして置かなければならない。

 
awk -f myprog -- -v -d file1 file2 …
  

これはgawkでは不要である。 `--posix'オプションが指定されないかぎり、 gawkは認識できないあらゆるオプションに対して なんのメッセージも出さずにそのままARGVへと格納し、 awkプログラムはそういったオプションを取り扱えるのである。 先の例はgawkでは以下のようにできる。

 
gawk -f myprog -d -v file1 file2 …
  

`-d'が正当なgawkのオプションでないので 後続する`-v'awkプログラムにそのまま渡すのである。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7. awk における配列

配列(array)は要素(elements)と呼ばれる値のテーブルであり、 配列の要素はその添え字によって区別される。 添え字(Indices)は数値でも文字列でも良い。

この章 では、awkにおいて配列がどのように働くか、 配列要素をどのように使うか、配列にある全要素をどのようにして走査するか、 配列の要素をどのように削除するかについて説明する。 また、awkがどのように多次元配列をシミュレートしているかと 配列の使用に関してあまり明確でないポイントについても述べる。 本章 はgawkの配列のその添え字によるソートの 能力についての記述で締めくくられる。

awkは変数に使える名前と、配列や関数 (see section ユーザー定義関数) に使える名前をひとまとめに管理している。 そのため、変数と配列で同じ名前を持つものを同時に使用することはできない。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.1 配列の序論

awkには一次元の配列があり、 文字列や数値を関係するグループ毎にまとめることができる。 awkの配列は名前を持たなければならない。 配列の名前は変数名と同じ文法である。 つまり、変数名として正しい名前であれば配列名としても(文法的に)正しい。 しかし、一つのプログラムの中で同時に配列と変数で同じ名前を使うことはできない (訳注: ふるーいgawk(2.11くらい?)では可能でした)。

表面的にはawkの配列は他のプログラミング言語の配列と似ているように見える。 しかし基本的な違いがある。それは、awkでは配列の大きさを使う前に特定する必要がなく、 それに加えて、連続した整数だけでなくどんな数字や文字列でも 配列の添字として使うことができるということである。

他の多くの言語では配列と、その要素数や要素の型を宣言(declared)しなければならない。 そういった言語では宣言によって、要求された要素数に見あうだけの連続している メモリブロックの確保が引き起こされる。配列の添字は正の整数でなければならない。 例えば、配列中で0で添字付けされる要素はメモリブロックの先頭にある配列の一番目の要素であり、 1で添字付けされる要素は二番目の要素であり一番目の要素の次に格納されている。 以下同様である。この方法では最初に宣言された分だけの場所しかないので、 配列に新たに要素を付け加えることはできない (一部の言語では、`15 .. 27'のように配列の下限と上限を指定することができる。 しかし、そのような言語でも配列の大きさはあらかじめ宣言した大きさに固定である)。

A contiguous array of four elements might look like the following example, conceptually, if the element values are 8, "foo", "", and 30: 概念的には8, "foo", "", 30 という四つの要素を持つ配列は次のようになるだろう。

値は単に格納されているだけであり、 添字はその大きさの順に並んでいるという暗黙のルールがある。 8は0で添字付けされる要素の値である。 なぜなら8 の前には一つの要素もないからである。

awkにおける配列は違っていて、連想的(associative)である。 つまり配列が、添字とそれが添字付けする配列要素のペアの集合であるということである。

 
要素 3     値 30
要素 1     値 "foo"
要素 0     値 8
要素 2     値 ""
  

ペアはごちゃごちゃの順番で並んでいるが、 それは添字による配列要素の順番付けが (連想配列では)意味のないことだからである。

連想配列の有利な所は、新しいペアを任意に作り出すことができるということである。 例えば先の配列に"number ten"という値を持つ10番目の要素を加えてみよう。 結果はこうなる。

 
要素 10    値 "number ten"
要素 3     値 30
要素 1     値 "foo"
要素 0     値 8
要素 2     値 ""
  

今この配列は(sparse、一部の要素がない)である。 要素として1 から4までと10を持っているが、 5, 6, 7, 8, 9 の要素は持っていない。

連想配列でもう一つの重要な点は、添字が正の整数に限られない。ということである。 どんな数値でもよいし、文字列であってもよい。 例えば、次の例はある単語を英語からフランス語に翻訳する配列である。

 
要素 "dog" 値 "chien"
要素 "cat" 値 "chat"
要素 "one" 値 "un"
要素 1     値 "un"
  

ここで数字の1が"one"のように使われていても翻訳できるようにしている。 つまり、同じ配列で添え字に数字と文字列を使うことができるということである (事実、配列の添え字は常に文字列として扱われる。 これに関しての詳細は配列の添え字として数値を使う)。 この例では1はダブルクォーテーションで囲まれていない。 それはawkがそれを自動的に文字列に変換するからである。

IGNORECASE の値は配列の添え字に関しては何の影響も及ぼさない。 配列要素を取り出すためにはその要素に格納するのに使ったのと全く同じ文字列を 使わなければならない。 awkがユーザーのために配列を作ったとき(例えば組み込み関数のsplitを使ったとき) は配列の添字は1から始まる連続した整数である (See section 文字列操作関数.)。

awkの配列参照は効率的である。 配列要素を参照するためのアクセス時間は配列の大きさとは無関係である。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.2 配列要素への参照

配列の使用において重要な点はその要素の一つをどのように参照するかである。 配列の参照は次のような式で行う。

 
array[index]
  

ここでarrayは配列の名前であり、 indexはアクセスしたい要素を配列中で添字付けする式である。

配列の参照によって得られる値はその配列要素のその時点での値である。 例えば、 foo[4.3]`4.3'で添字付けされた配列fooの要素を取り出す式である。

何も値の入っていない配列要素を参照すると、空文字列 "" がその値であるとして取り出される。 このことは何も値を代入していない配列要素や削除された配列要素にも言える (see section delete)。 このような参照を行うと自動的に空文字列を値とする配列要素が自動的に作られる (これはawk内部でのメモリの浪費を招き、時として不運な結果となる)。

配列中にある添字で指定される要素があるかどうかを次のようにして調べることができる。

 
index in array
  

この式は(存在しない添字の要素をアクセスしようとして新たな配列要素を作ってしまう) 副作用を引き起こすことなしに、配列中に特定の添字が存在するかどうかをテストする。 式の値はarray[index]が存在すれば1(真)、存在しなければ0(偽)となる。 例えばfrequenciesという配列に `2'で添字付けされるものがあるかどうかは次のように書けばよい。

 
if (2 in frequencies)
    print "Subscript 2 is present."
  

これはfrequenciesという配列の中に`2' というを持った要素があるかどうかを調べるのではなく (そういったことを調べるには配列全体をスキャンする以外にない)、 また、frequencies[2] という要素を作るようなこともしない。 完全には同じではないが、次の例と同じ事である。

 
if (frequencies[2] != "")
    print "Subscript 2 is present."
  

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.3 配列要素の代入

配列要素はawkの普通の変数と同じ様に代入することができる。

 
array[subscript] = value
  

ここでarrayは配列の名前であり、 subscriptという式は代入先の配列要素を指定する添え字である。 valueという式は配列要素に代入する値である。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.4 基本的な配列の例

次に挙げたプログラムは、行番号で始まる行のリストを入力として受け取り、 行番号順にそれらを出力するものである。 行番号は入力時には順番になっておらずごちゃごちゃになっている。 このプログラムはそれらの行を行番号を添字として配列を使うことによって行のソートを行っている。 そのため、行の出力は行番号の順番に行われるようになっている。 これは非常に単純なプログラムであり、同じ番号が出てきたり、番号に抜けがあったり、 行が番号で始まっていなかったりすると混乱してしまう。

 
{
  if ($1 > max)
    max = $1
  arr[$1] = $0
}

END {
  for (x = 1; x <= max; x++)
    print arr[x]
}
  

最初のルールではそれまでに見つかった最大の行番号を記録している。 同時に、各行をarrという配列に行番号を添え字に使って記録している。 二番目のルールはすべての入力が読み込まれたあとで、すべての行を出力するために実行される。 このプログラムに次のような入力を与えたとすると

 
5  I am the Five man
2  Who are you?  The new number two!
4  . . . And four on the floor
1  Who is number one?
3  I three you.
  

出力はこうなる。

 
1  Who is number one?
2  Who are you?  The new number two!
3  I three you.
4  . . . And four on the floor
5  I am the Five man
  

番号が繰り返して現れた場合、最後の行が(同じ番号の)他のものを上書きする。 行番号に抜けが合った場合も、 プログラム中のENDルールの簡単な修正によって対処できる。

 
END {
  for (x = 1; x <= max; x++)
    if (x in arr)
      print arr[x]
}
  

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.5 配列の全要素を走査する

プログラムで配列を使っていると、 配列の各要素に対して何等かの処理を行うようなループを実行する必要にせまられる。 そういった操作は、他の言語では配列はその添え字が正の整数に限定されるので簡単である。 つまり、配列の最大の添字はその配列の長さよりも短く、 ゼロから添え字を数え上げていけば全ての要素を参照できる。 しかしこの技法はawkでは使えない。 なぜなら配列の添字が整数に限定されず、文字列の場合もあるからである。 そのため、awkは配列のスキャンのために特別な for文を備えている。

 
for (var in array)
  body
  

このループはbodyをプログラム中で、 それまでにarrayで添え字付けするのに使ったそれぞれの値に対して一度ずつ実行される。 ここで変数varにはその添え字がセットされる。

See section 文字列操作関数, for more information on the built-in function length. 次のプログラムは今説明したようなfor文を使用している。 最初のルールでは入力レコードをスキャンし、 そこで(少なくとも一度)見付かった単語を添え字とした usedの要素に1を代入する。 二番目のルールでusedの要素をスキャンして入力から見付かった単語をすべて検索し、 その単語が10文字よりも長ければ出力し、最後にそういった単語の合計数も出力する。 組み込み関数lengthの詳しい説明は See section 文字列操作関数 にある。

 
# 少なくとも一回使われた単語に対してそれぞれ 1 を記録する
{
    for (i = 1; i <= NF; i++)
        used[$i] = 1
}

# 十文字以上の長さの単語が幾つあるか調べる
END {
    for (x in used)
        if (length(x) > 10) {
            ++num_long_words
            print x
        }
    print num_long_words, "個の単語が十文字を越えていた"
}
  

See section Generating Word-Usage Counts, for a more detailed example of this type.

この文を使ってアクセスされる配列中の要素の並びは awk内部での配列要素の配置によって決定され、 それを制御したり変更することはできない。 このことは、 bodyの中にある文で arrayに新しい要素を付け加えたときに 問題を引き起こす可能性がある。 forループが付け加えられた要素に到達するかどうかも分からない。 同様に、ループの中でvarを変更すると何が起こるか分からない。 最善の方法はそんなことをしないということである。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.6 delete

個々の配列の要素を削除するには、delete 文を使用する:

 
delete array[index]
  

要素を削除した後ではその要素に対する参照は行えない。 削除したものに対する参照は、 何も値を設定していなくて一回も参照していない配列に対する参照と同じである。

 
for (i in frequencies)
  delete frequencies[i]
  

この例はfrequenciesという配列中の要素を全て削除する。 要素を削除すると、for文を使って配列をスキャンしても (削除された) 要素は存在しないと報告され、 in 演算子を使ってチェックしても 0 (偽、false)が返ってくる。

 
delete foo[4]
if (4 in foo)
    print "これは決して出力されない"
  

配列の要素を削除することとnull(空文字列, "") を代入することが違うということは重要である。

 
foo[4] = ""
if (4 in foo)
  print "これはfoo[4]が空であっても出力される"
  

存在しない要素を削除してもエラーにならない。 `--lint'がコマンドラインで与えられたとき(see section コマンドラインオプション)、 gawkは配列にない要素が削除されたときに警告を発する。

以下のようにdelete文で添え字付けを行わないことによって、 その配列のすべての要素を一つの文で削除することができる。

 
delete array
  

この機能は gawk の拡張であり、互換モード(see section コマンドラインオプション) では使うことはできない。

この形式のdelete文を使うのは、 ループを使って配列の要素の削除を行うものより三倍以上効率的である。

次の例は移植性のあるやり方であるが、配列をクリアするのにわかりやすいやり方ではない (30)

 
split("", array)
  

split関数 (see section 文字列操作関数) は目的の配列を最初にクリアする。 この呼び出しは空文字列を分割するということであり、 したがって分割すべきデータはなく、 関数は単に配列をクリアして呼び出し元に復帰するのである。

警告: 配列の削除はその型を変更しない。配列を削除し、その後で その配列の名前をスカラー(つまり通常の変数)として使うことはできない。 たとえば、以下の例は動作しない:

 
a[1] = 3; delete a; a = 3
  

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.7 配列の添え字として数値を使う

配列について重要なのは、配列の添え字は常に文字列として扱われるということである。 配列の添え字に数字を使った場合、それは添え字付けに使われる前に文字列に変換される (see section 文字列と数値の変換)。 これは、組込み変数CONVFMTの値がプログラム中での配列要素に対するアクセスに 影響を及ぼす可能性があると言うことである。例えば、

 
xyz = 12.153
data[xyz] = 1
CONVFMT = "%2.2f"
if (xyz in data)
    printf "%s は data にある\n", xyz
else
    printf "%s は data にない\n", xyz
  

これは`12.15 は data にありません'を出力する。 最初の文ではxyzに数値データを代入している。 data[xyz]に対する代入は、 文字列値"12.153"dataを 添え字付けし (CONVFMTで指定されるデフォルトの変換ルール"%.6g"による)、 結果としてdata["12.153"]に 1が代入される。 プログラムはその後でCONVFMTを変更している。 (xyz in data)というテストは新たにxyzyの文字列への変換を行うが、 このときの結果は "12.15"となる。 なぜならCONVFMTに設定された値によって小数点以下二桁までしか許されていないからである。 "12.15""12.153"は違うのでこのテストは失敗する。

変換ルールに従うと(see section 文字列と数値の変換)、 整数値は常に整数を表わす文字列に変換され、 CONVFMTの値は変換に対して影響を与えない。 良くある例では、

 
for (i = 1; i <= maxsub; i++)
    do something with array[i]
  

CONVFMTの値が意味を持たないのでこれはうまく動作する。

``整数は常に整数として文字列化される''というルールは 配列の添え字付けに適用される。 八進定数や十六進定数(see section 八進数字および十六進数字)は内部的に数値に変換されて もともとの形式は忘れ去られてしまう。 つまり、array[17]array[021]array[0x11]は すべて同じ要素を参照するのである!

awkでの多くの事象のように大抵の場合は期待通りに動作する。 しかし、これは実際のルールの正確な知識を得るのに有益であり、 時として微妙な効果をプログラムに対して及ぼすことがある。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.8 添え字として初期化されていない変数を使う

ここで入力されたデータを逆順に出力したいと考えたとしよう。 これを行う合理的な方法は次のようなものになるだろう (サンプルデータも付いている)。

 
$ echo 'line 1
> line 2
> line 3' | awk '{ l[lines] = $0; ++lines }
> END {
>     for (i = lines-1; i >= 0; --i)
>        print l[i]
> }'
-| line 3
-| line 2
  

残念なことに入力データの中で一番最初の行は出力されていない!

一見するとこのプログラムはちゃんと動作するようにみえる。 linesは初期化されておらず、初期化されていない変数は数値 0の値を持っている。 だから、awkl[0]の値を出力するはずだ。

結論をいうと、awkの配列で使う添え字は常に文字列として扱われる。 また、初期化されていない変数が文字列として扱われたときの値は "" であって、0ではない。 そのため、`line 1'は最終的にはl[""]に格納されたのだ。 次の例はちゃんと働くようにしたプログラムである。

 
{ l[lines++] = $0 }
END {
    for (i = lines - 1; i >= 0; --i)
       print l[i]
}
  

ここで、`++'はlineを強制的に数値にする。 したがって、"古い値"(old value)を数値の 0にする。 これは配列の添え字として(文字列の)"0"に変換する。

今見たように、それが幾分一般的でないものであったとしても、空文字列("") は配列の添え字として正当なものなのである (d.c.) 。 コマンドラインで`--lint'が指定されていた場合 (see section コマンドラインオプション)、 gawkは配列の添え字に空文字列を使うと警告を発する。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.9 多次元配列

多次元配列とは、配列要素の指定を複数の添え字の並びによって行う配列である。 例えば二次元の配列は二つの添え字を必要とする。 一般的な(awkも含めた大多数の言語では) 二次元配列の要素に対する参照は grid[x,y]のように行う (gridは二次元配列の名前)。

awkでは、 多次元配列をその添字を一つの文字列に連結して扱うということで実現している。 つまり、添字を文字列に変換し (see section 文字列と数値の変換)、 それをセパレータを間にはさんで一つに連結する。 ということである。 ここで一つにまとめられた文字列が多次元配列の添え字となり、 この文字列は通常の一次元配列に対する添え字であるように扱われる。 セパレータには組み込み変数SUBSEPに格納されている値が使われる。

例えば、SUBSEPの値が"@"であるとき `foo[5,12]="value"'という式を評価するとしよう。 5と12という数値は文字列に変換され、そしてその間に `@'をはさんで連結する。 その結果は"5@12"となる。 したがって、 foo["5@12"]という要素に"value" がセットされるという結果になるのである。

一度配列要素に格納してしまうと、awkはその添字が一つのインデックスなのか、 複数のインデックスの並びなのかの区別がつけられない。 このため、 `foo[5,12]'`foo[5 SUBSEP 12]' の二つの式は常に同じ値となる。

SUBSEPのデフォルトの値は"\034"という文字列である。 このキャラクタは印字可能キャラクタでなく、 awkプログラムや入力データ中に現れることはまずないであろうキャラクタである。 ありそうもないキャラクタを選択するということは、 SUBSEPの値によっては区別を付けられないような 添え字文字列を作り出してしまうことがあるので、 それを避けるために有効である。 たとえばSUBSEP"@"だったとすると、 `foo["a@b","c"]'`foo["a","b@c"]'は両方とも結果的に `foo["a@b@c"]'となるので区別することができない。

ある添え字の並びが"多次元"配列中に存在するかどうかは 一次元配列の時と同じ様に `in'演算子を使ってできる。 具体的には、`in'演算子の左辺に全体が括弧でくくられているカンマで 各添え字を区切った添字の並びを置けばよい。

 
(subscript1, subscript2, …) in array
  

次に挙げる例では入力を二次元配列のフィールドであるかのように扱い、 配列を90度時計まわりに回転させてその結果を出力する。 この例では全ての行において要素の数が同じであると仮定している。

 
{
     if (max_nf < NF)
          max_nf = NF
     max_nr = NR
     for (x = 1; x <= NF; x++)
          vector[x, NR] = $x
}

END {
     for (x = 1; x <= max_nf; x++) {
          for (y = max_nr; y >= 1; --y)
               printf("%s ", vector[x, y])
          printf("\n")
     }
}
  

入力として次のデータを与える

 
1 2 3 4 5 6
2 3 4 5 6 1
3 4 5 6 1 2
4 5 6 1 2 3
  

結果は次のようになる。

 
4 3 2 1
5 4 3 2
6 5 4 3
1 6 5 4
2 1 6 5
3 2 1 6
  

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.10 多次元配列の走査

"多次元"配列を走査するための特殊なfor文はない。 というのは、多次元の配列やその要素というものは存在していなくて、 配列に対する多次元的なアクセス方法しかないからである。

しかし、プログラム中でその(検査を行いたい)配列を 常に多次元配列としてアクセスをしているのであれば、 for文と (see section 配列の全要素を走査する) 、 組み込み関数split(see section 文字列操作関数) を組み合わせて使うことによって配列のスキャンを行うことができる。 具体的にはこうする。

 
for (combined in array) {
    split(combined, separate, SUBSEP)
    …
}
  

これは連結された状態の配列の添え字をcombinedに取りだし、 それをSUBSEP の値がある場所を基準にして独立した添え字に分割している。 分割された添え字は配列 separateの要素となる。

したがって、以前にarray[1,"foo"]に値を格納したとすると、 実際にはarrayの中では"1\034foo"で添字付けされる要素に対して行なわれている。 (SUBSEPのデフォルトの値が034というキャラクタコードであったことを思い出そう) 遅かれ早かれfor文は繰り返しの中で"1\034foo"という連結された添字を見つけ出す。 そしてsplit関数が次のように呼ばれる。

 
split("1\034foo", separate, "\034")
  

この結果、separate[1]"1"が、 separate[2]"foo"がセットされる。 こうすることによって、元々の分割された添字の並びを取り出すことができる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.11 gawk で配列の値や添え字をソートする

`for (i in array)'ループを使って配列を操作する順序は本質的に 気まぐれなものである。 ほとんどのawkの実装では、配列のソートにはsort関数を 記述する必要があった。 これは異なるソートアルゴリズムを説明するための教育には良いが、 通常はプログラムのポイントとはならない。 gawk は配列のソートのための組み込み関数asortasortiを 提供している(see section 文字列操作関数)。たとえば:

 
populate the array data
n = asort(data)
for (i = 1; i <= n; i++)
    do something with data[i]
  

asortを呼び出した後、配列dataは1からdataの要素数である n(この数値はasortの戻り値)まで添え字付けされる。 data[1]data[2]data[3]...のようになる。 配列要素の比較はgawkの通常の比較ルールを使って行われる (see section 変数の型付けと比較式)。

asortの重要な副作用は配列の元の添え字は完全に失われるという ことである。これは常には望まれる動作ではないので、asortは 第二引数を受け取ることができる:

 
populate the array source
n = asort(source, dest)
for (i = 1; i <= n; i++)
    do something with dest[i]
  

この場合、gawkは配列sourcedestにコピーしてから destをソートしてその添え字を破壊する。しかし、配列sourceは 影響を受けない。

要素の値ではなく添え字でソートを行う必要に迫られることがよくあるだろう。 これを行うためには、gawk 3.1.2 で導入された関数@code{asorti}を 使う。そのインターフェースはasortと同一であるが、 ソートのために添え字の値を使い、結果の配列の値となる点が異なる。

 
{ source[$0] = some_func($0) }

END {
    n = asorti(source, dest)
    for (i = 1; i <= n; i++) {
        do something with dest[i]             添え字を直接使って操作するdo something with source[dest[i]]     ソートされた添え字を使ってオリジナルの配列にアクセス
    }
}
  

使っているgawkのバージョンが3.1.0もしくは3.1.1であるならば、 asortiはない。代わりに、ソートされた添え字の値を保持する ヘルパー配列を使い、それから元の配列の要素にアクセスする。これは 以下のようにして行う:

 
populate the array data
# copy indices
j = 1
for (i in data) {
    ind[j] = i    # 添え字の値が要素の値となる
    j++
}
n = asort(ind)    # 添え字の値がソートされた
for (i = 1; i <= n; i++) {
    do something with dest[i]             添え字を直接使って操作するdo something with source[dest[i]]     ソートされた添え字を使ってオリジナルの配列にアクセス
}
  

添え字の置き換えによる配列のソートは最大限のフレキシビリティをもたらす。 降順に要素をたどるには、nから1へと減っていくループを使う。

配列の添え字や要素をコピーすることはメモリの使用量において高価なものではない。 内部的に、gawkはデータの参照カウントを保持している。 たとえば、asortが最初の配列を二番目の配列にコピーしたとき、 両方の配列で値を使っていたとしても オリジナルの配列要素のデータのコピー一つしかない。 同様に、dataからindに添え字をコピーしたときも 実際の添え字文字列のコピーは一つしか存在しない。

先に、比較においては gawk の``通常の比較ルール''が使われると述べた。 IGNORECASE は文字列比較に影響を及ぼすので、IGNORECASEの 値は asort および asorti のソートにも影響を及ぼす。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8. 関数

この章 では、awkの三つのカテゴリー (数値、文字列、入出力)の組み込み関数について説明する。 gawkはさらに時刻、ビット操作、国際化、 ローカライズについての関数群を提供している。

組み込み関数のほかに、awkには プログラムのほかの部分で使用できる新たな関数を記述するための 用意がある。この章 の後半では、そのような ユーザー定義(user-defined)関数について説明する。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1 組み込み関数

組み込み(built-in)関数とはawkプログラムで常に呼び出す事のできる関数である。 このセクション ではawkの全ての組み込み関数を定義する。 一部のものは別のセクションで説明がされているが、 便宜のためここでも簡単にまとめてある


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.1 組み込み関数を呼び出す

awkの組み込み関数を呼び出すには、 関数の名前に続けて括弧で囲まれた引数を書けばよい。 例えば`atan2(y + z, 1)'は関数atan2 を二つの引数で呼び出す。

組み込み関数の名前と、開き括弧の間にある空白は無視される。 しかし、そういった空白は使わないようにすることを勧めたい。 ユーザー定義の関数ではこのような空白を許されておらず、 関数名の直後に空白をおかないという単純なやり方でそのような間違いを簡単に見つけ出せる。

組み込み関数はそれぞれ、特定の数の引数を受け取る。 多くの場合、組み込み関数に対して余計に多く渡された引数は無視される。 引数が省略されたときのデフォルトの扱いは個々の関数毎に決められている。 一部のawk処理系では、 組込み変数に余計な引数を渡した場合はそういった引数は無視されていた。 しかしgawkでは、それは致命的なエラーとなる。

関数が呼び出されるとき、関数の実引数は実際に関数が呼び出される前に完全に評価され、 式が作り出される。例えば次のようなコードでは

 
i = 4
j = sqrt(i++)
  

変数isqrtに対する実引数の値として 4がセットされ、関数が呼ばれる前にインクリメントされて値が5となる。 関数の引数として遣われる式の評価の順序は未定義である。 そのため、 実引数が左から右へ順番に評価されるということを仮定したプログラムを書くべきではない。 例えば、

 
i = 5
j = atan2(i++, i *= 2)
  

上記のようなプログラム片があったとして、評価の順番が左から右であったとすると、 iは最初に6になり、ついで12となり、 それからatan2が実引数6と12で呼び出される。 しかし、評価が右から左へ行われたとすると、 iは最初に10となり、ついで11になる。 その後atan2の呼び出しが実引数11と10で行われる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.2 数値関数

以下のリストは、数値を扱うすべての組込み関数である。 省略可能な引数はブラケット([ ])で囲まれている。

int(x)

この関数はxに最も近い整数への丸めを行う。 丸めのときは0へ向かって、つまり0とxの間にある整数の中から選択する。

例えば、int(3) は 3、 int(3.9) は 3、 int(-3.9) は -3、そして int(-3) は -3 となる。

sqrt(x)

この関数はxの正の平方根を返す。 xが負であった場合にはエラーとなる。 たとえば、sqrt(4)は2である。

exp(x)

これは自然対数の底ex乗を返すか(e ^ x)、 xが範囲外であればエラーをレポートする。 xの取り得る値の範囲はあなたが使っているマシンの浮動小数点数の実装による。

log(x)

xが正の場合にはxの自然対数を返し、そうでない場合にはエラーとなる。

sin(x)

xの正弦を返す(xの単位はラジアン)。

cos(x)

This returns the cosine of x, with x in radians. xの余弦を返す(xの単位はラジアン)。

atan2(y, x)

y / xのアークタンジェントを返す(単位はラジアン)。

rand()

乱数を返す。randの返す値は0から1の範囲の数であり、0は含まれるが1は含まれない。(31)

しばしば整数の乱数を必要とするだろうが、 次に n未満の非負の整数の乱数を返すユーザー定義関数を挙げる。

 
function randint(n) {
     return int(n * rand())
}
   

この例での掛け算は0より大きく、n未満の実数を作り出す。 それを(int を使って)0からn - 1の間の整数にする。

次の例は先ほどのものと同じ様な、1からnの間の整数を返す関数を使っている。 このプログラムはレコード入力の度に新しい乱数を出力する。

 
# さいころをシミュレートする関数
function roll(n) { return 1 + int(rand() * n) }

# 三つの六面体サイコロを振り、
# その合計を出力する。
{
      printf("%d points\n",
             roll(6)+roll(6)+roll(6))
}
   

注意: gawkも含め大部分のawk処理系で実行の度に randは、同じ数値、もしくは種から数値を作り出しはじめる。 このことはプログラムを実行する度に同じ結果が生成されるということである。 その数値はひとつのawkプログラムの中ではランダムであるが 走らせるごとに予想できるものである。 これはデバッグには便利であるけれども、 使う度毎に違う結果を必要とするような場合には実行する毎に 乱数の種を違ったものにしなければならない。それを行うにはsrandを使用する。

srand([x])

srandは乱数を生成するための出発点、もしくは種の値として xをセットする。

それぞれの種は各々特定の乱数列を導き出す。(32) 従って、乱数の種として同じ値を二度目にセットしたとすると、 同じ乱数列を得ることになる。

異なるawk処理系では異なる乱数生成器が使用される。 同一のawkプログラムが異なるawkで実行したときに 同じ乱数列を生成するとは期待しないこと。

`srand()'の引数xを省略した場合、乱数の種としてその時点の日時が使用される。 この方法は予測できないような乱数列を得られる。

srandの返す値は以前使われていた乱数の種である。 これによって以前の乱数系列をもう一度作り出すことが簡単になる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.3 文字列操作関数

このセクション にある関数は一つ以上の文字列を検索したり変更したりするものである。 省略可能な引数はブラケット([ ])で囲まれている。

asort(source [, dest]) #

asortgawk特有の拡張で、配列sourceの要素数を返す。 sourceの内容はgawkの通常の値比較のルールを使って ソートされる(IGNORECASEがソートに影響する)。そしてsourceの ソートされた値はシーケンシャルな整数値によるものに置き換えられる。 省略可能な配列destが渡された場合、sourcedestに 複写される。destはそれからソートされ、sourceは変更されない。 たとえば、aの内容が以下のようなものであったとする:

 
a["last"] = "de"
a["first"] = "sac"
a["middle"] = "cul"
   

asortを呼び出す:

 
asort(a)
   

aは次のようなものとなる:

 
a[1] = "cul"
a[2] = "de"
a[3] = "sac"
   

関数asortgawk で配列の値や添え字をソートするに詳しく説明されている。 asortgawkの拡張で、互換モード(see section コマンドラインオプション)で 使うことはできない。

asorti(source [, dest]) #

IGNORECASE affects the sorting.) asortigawk特有の拡張で、配列sourceの要素数を返す。 asortと同じように動作するが、値ではなく添え字がソートされる。 配列の添え字は常に文字列であるので、比較は常に文字列比較で行われる (これもまたIGNORECASEがソートに影響する)。

関数asortigawk で配列の値や添え字をソートするで詳細が説明されている。 これはgawk 3.1.2 で追加された。 asortigawkの拡張であり、互換モード(see section コマンドラインオプション)で 使うことはできない。

index(in, find)

文字列inの中で文字列findが出てくる最初の場所を検索し、 見つかった文字列が始まるinのキャラクタの位置を返す。例を挙げよう。

 
$ awk 'BEGIN { print index("peanut", "an") }'
-| 3
   

もしfindが見つからなかったなら、indexは0を返す (awkでの文字列の添字は1から始まると言うことを思い出して欲しい)。

length([string])

string中のキャラクタの数を返す。 stringが数値であった場合、 その数値を(その数値を表す)文字列に変換したときのキャラクタの数が返る。 例えば、 length("abcde")は5であるが、 length(15 * 35)の結果は 3である。 それは15 × 35 = 525で、この525は三つのキャラクタからなる "525"という文字列に変換されるからである。

引数が省略された場合、length$0の長さを返す。

注意 : 古いバージョンのawkでは、length関数を括弧なしで呼ぶことができる。 それは POSIX の標準では"deprecated"(非推奨)とされる。 このことは、プログラム中でこういった書き方が将来のバージョンでは 使えなくなるかも知れないということである。 従って、awkプログラムの移植性を最大にするために括弧を常に書くべきである。

gawkのバージョン 3.2以降では、引数に配列を渡したときに length関数はその配列の要素数を返すようになる。 配列は1からその要素数までの数字で添え字付けされるものではないので これは最初に考えたより便利ではない。 `--lint'がコマンドラインで指定されたとき(see section コマンドラインオプション)、 gawkは配列を引数とすることは互換性にかけるものとして警告する。 `--posix'が指定されたときには、配列を引数とすることは 致命的エラーとなる(see section awk における配列)。

match(string, regexp [, array])

match関数はstringから、正規表現regexpにマッチする部分文字列の中で、 最も左にあってもっとも長い部分文字列を検索し、 部分文字列が始まる場所を返す(stringの最初から始まっていれば1)。 マッチするものが見つからなかった場合、0を返す。

regexp引数は正規表現定数(`/…/'か 文字列定数("…"))のいずれかであってよい。 後者の場合にはその文字列はマッチ対象の正規表現として扱われる。 これら二つの形式の間の相違については動的正規表現を使う

最初の二つの引数の並びは他の正規表現を使用する文字列関数(subgsub)とは逆である。matchでの順番を思い出すには その並びが`~'演算子と同じ(`string ~ regexp') であることが助けになるだろう。

The match function sets the built-in variable RSTART to the index. It also sets the built-in variable RLENGTH to the length in characters of the matched substring. If no match is found, RSTART is set to zero, and RLENGTH to -1. match関数は組み込み変数のRSTART にインデックスをセットし、 同様に組み込み変数RLENGTHにマッチした部分文字列の長さをセットする。 マッチしなかった場合には、RSTART には0が、RLENGTH には -1がセットされる。

For example: 例えば、

 
{
       if ($1 == "FIND")
         regex = $2
       else {
         where = match($0, regex)
         if (where != 0)
           print "Match of", regex, "found at",
                     where, "in", $0
       }
}
   

このプログラムは変数regexに格納されている正規表現にマッチする行を探す。 この正規表現は変更することができる。ある行の最初の単語が`FIND'であった場合、 regexはその行の二番目の単語に変更される。 従って、次のようなデータを与えると

 
FIND ru+n
My program runs
but not very quickly
FIND Melvin
JF+KM
This line is property of Reality Engineering Co.
Melvin was here.
   

awkの出力は次のようになる。

 
Match of ru+n found at 12 in My program runs
Match of Melvin found at 1 in Melvin was here.
   

arrayが与えられたとき、 それはクリアされ、その後でその0番目の要素にregexpにマッチした stringの部分文字列全体が格納される。 regexpにカッコがあれば、arrayの整数によって添え字付けされた要素に stringでカッコに対応している部分が格納される。 例を挙げよう。

 
$ echo foooobazbarrrrr |
> gawk '{ match($0, /(fo+).+(bar*)/, arr)
>           print arr[1], arr[2] }'
-| foooo barrrrr
   

これに加えて、gawk 3.1.2 では、マッチした部分式の 開始位置の添え字と長さを格納する多次元配列を使うことができる:

 
$ echo foooobazbarrrrr |
> gawk '{ match($0, /(fo+).+(bar*)/, arr)
>           print arr[1], arr[2]
>           print arr[1, "start"], arr[1, "length"]
>           print arr[2, "start"], arr[2, "length"]
> }'
-| foooo barrrrr
-| 1 5
-| 9 7
   

マッチしたテキストを持っていない可能性があるので、 すべての括弧付けされた部分式に対してstartとindexのための添え字が 存在しているとは限らない。したがって、in 演算子 (see section 配列要素への参照)を使ってテストしたほうが良いだろう。

matchに対するarray引数はgawkの拡張である。 互換モード(see section コマンドラインオプション)では、第三引数を使うと致命的 エラーとなる。

split(string, array [, fieldsep])

この関数はstringfieldsepによって分割し、 分割された結果を arrayに格納する。 分割された最初の要素はarray[1]に、 二番目の要素は array[2]に格納され、以下の要素も同様である。 三番目の引数fieldsepの文字列値はstringを分割する場所を指定する (FSが入力レコードを分割する場所にマッチする正規表現であるように)正規表現である。 fieldsepが省略されると、FSの値が使われる。 splitは作り出された要素の数を返す。

split関数はまた、 入力行をフィールドに分割するとの同じ様なやり方で文字列を分割する。 例えば、

 
split("cul-de-sac", a, "-")
   

これは`-'をセパレータとして`auto-da-fe'という文字列を三つのフィールドに分割し、 配列aの要素を以下のようにセットする。

 
a[1] = "cul"
a[2] = "de"
a[3] = "sac"
   

この split の呼び出しは3を返す。

fieldsepの値が" " である場合には先頭や末尾にある空白は無視され、 要素は空白で区切られる。 また同様に、fieldsepが空文字列であれば、 個々のキャラクタが配列の要素となるように分割がおこなわれる (これはgawk特有の拡張である)。

しかしながら注意して欲しいのは、RSsplitが動作するのに 何の影響も及ぼさないということである。`RS = ""'であり改行も 入力フィールドセパレータであったとしてもそれはsplitが 文字列を分割するのには影響しない。

最近のawk処理系ではgawkも含めて、 この関数の第三引数に文字列だけでなく/abc/のような正規表現定数を許している。 (d.c.) POSIXの標準も同様である。 動的正規表現を使うでは文字列定数と正規表現定数との間の違いについて述べている。

文字列の分割に先立って、splitは配列arrayの要素をすべて削除する。

stringが空文字列であった場合、その配列は要素を持たない (このためこれは一文で配列全体を削除する移植性のある方法である。 See section delete)。

stringのどこにもfieldsepにマッチするものがない場合 (ただし空文字列ではない)、 arrayはただ一つの要素を持つようになる。 その要素の値は元々のstringと同じである。

sprintf(format, expression1, …)

この関数はprintfがその引数を渡されたときに出力するであろう文字列を (出力はせずに)返す (see section printfを使って出力を整える)。 例えば、

 
pival = sprintf("pi = %.2f (approx.)", 22/7)
   

これは変数pival"pi = 3.14 (approx.)"という文字列をセットする。

strtonum(str) #

strを検査し、その数としての値を返す。str`0'で 始まっていた場合、strtonumstrが八進数値であると仮定する。 str`0x'もしくは`0X'で始まっていた場合には、 strtonumstrが十六進数値であると仮定する。 例を挙げよう:

 
$ echo 0x11 |
> gawk '{ printf "%d\n", strtonum($1) }'
-| 17
   

strtonum関数を使うことは文字列にゼロを付加することと 同じではない。文字列の数値への自動変換は十進データに対してのみ 働き、八進や十六進データには働かない。(33)

strtonumは数値を認識するのにカレントロカールの小数点を使用することにも注意すること。

strtonumgawkの拡張である。 互換モード(see section コマンドラインオプション)のときには使うことはできない。

sub(regexp, replacement [, target])

sub関数はtargetの値を変更する。 検索する値は文字列でなければならず、 そしてそれは文字列中でregexpで与えられる正規表現にマッチする部分文字列の中で 一番左にあり長さが一番長いものがreplacementで置き換えられる。 置き換えられた文字列はtargetの新しい値となる。

引数regexpは正規表現定数(`/…/')か 文字列定数("…")のいずれかの形式をとる。 後者の場合文字列はマッチする正規表現として扱われる。 これら二つの形式の間の違いは動的正規表現を使うで述べられている。

この関数は独特である。それは、targetが単に値を計算するのに使われるのではなく、 式ではないということからくる。 targetは変数、フィールド、 配列の参照のように変更された値を格納できるものでなければならない。 この引数が省略された場合にはデフォルトとして$0が使用される。 (34) 例を挙げよう。

 
str = "water, water, everywhere"
sub(/at/, "ith", str)
   

この例では str"wither, water, everywhere"をセットし、 その中で最も左にあり、もっとも長い`at'`ith'に置き換える。

sub関数は置き換えを行った数(つまり1か0のいずれか)を返す。

replacement中にスペシャルキャラクタ`&'があると、 それはregexpにマッチした部分文字列を表す (もしこの正規表現が二つ以上の文字列にマッチするのならば、 `&'が表す部分文字列が変化するだろう)。 例えば、

 
{ sub(/candidate/, "& and his wife"); print }
   

これは各入力行で最初に現れる`candidate'`candidate and his wife'に変更する。 別の例を挙げよう。

 
$ awk 'BEGIN {
>         str = "daabaaa"
>         sub(/a+/, "C&C", str)
>         print str
> }'
-| dCaaCbaaa
   

This shows how `&' can represent a nonconstant string and also illustrates the ``leftmost, longest'' rule in regexp matching (see section どのくらいテキストがマッチするか?). この例では`&'は非文字定数として扱われ、"最左最長"の規則に従っている (see section どのくらいテキストがマッチするか?)。

スペシャルキャラクタ(`&')の効果は その前にバックスラッシュを付けることによって抑制することができる。 例によって、文字列中に一つのバックスラッシュを入れるためには バックスラッシュを二つ続けて書かなければならない。 従って、置換文字列中に`&'という文字を含ませるには`\\&'と記述する。 次に挙げる例は各行で最初に現れる`|'`&'で置き換える。

 
{ sub(/\|/, "\\&"); print }
   

上述のように、subの三番目の引数は左辺値でなければならない。 一部のawk処理系には三番目の引数として左辺値でない式を許すものもある。 そういった場合、subはパターンを検索し、0か1を返すけれども、 置き換え(起こったとして)の結果はそれを格納する場所がないので失われる。 そのような awkは次のような式を受け付ける。

 
sub(/USA/, "United States", "the USA and Canada")
   

過去のものに対する互換性のために、 gawkはこのような間違ったコードを受容する。 しかしながら、第三引数として別の変更不能なオブジェクトを使った場合には 致命的エラーを引き起こす結果となり、プログラムを実行することができないだろう。

結局のところ、regexpは正規表現定数ではなく、 文字列へと変換されその文字列値がマッチすべき正規表現として取り扱われるものである。

gsub(regexp, replacement [, target])

この関数はsubと似ているが、 gsubはもっとも長く最も左にあり、 それぞれが重ならないようなマッチした部分文字列をすべて置換する。 gsub`g'は全ての場所で置換を行う``global''を意味する。 次の例では、

 
{ gsub(/Britain/, "United Kingdom"); print }
   

全ての入力レコードで、`Britain'という文字列を全て `United Kingdom' に置き換える。

gsub関数は置き換えが起こった回数を返す。 検索と置換の対象となる変数 targetが省略された場合には入力レコード全体、 つまり$0が使用される。 subと同じ様に、`&'`\'の二つのキャラクタは特殊な意味があり、 また三番目の引数は代入可能なものでなければならない。

gensub(regexp, replacement, how [, target]) #

gensubは一般的な置換関数である。subgsub に 似て、ターゲット文字列 target 中で正規表現 regexp にマッチする ものを検索する。subgsub と異なり、変更された文字列は 関数の結果として返され、オリジナルのターゲット文字列は変更されないhow`g' もしくは `G' で始まる文字列であった場合には regexp にマッチするすべてが replacement で置き換えられる。 その一方で how は 置き換えるべき regexpのマッチを指示する 数値である。target が省略された場合には $0 が使われる。

gensubsubgsub にはない機能が付加されている: 置換テキストの中で正規表現の一部を指定することができるのである。 これはコンポーネントをマークするために正規表現中で括弧を使い、 置換テキストで `\N' を指定することで行われる。ここで、 N は 1 から 9 の数字である。例を挙げよう:

 
$ gawk '
> BEGIN {
>      a = "abc def"
>      b = gensub(/(.+) (.+)/, "\\2 \\1", "g", a)
>      print b
> }'
-| def abc
   

sub と同様、文字列中で一つのバックスラッシュを得るために 二つのバックスラッシュをタイプしなければならない。 置換テキストの中では、`\0' という並びは `&' と同様にマッチしたテキスト全体をあらわす。

以下の例はマッチした正規表現が変更すべきかどうかを制御する第三引数の 使用例である:

 
$ echo a b c a b c |
> gawk '{ print gensub(/a/, "AA", 2) }'
-| a b c AA b c
   

この場合、デフォルトのターゲット文字列として $0が使われる。 gensub は 結果として新しい文字列を返し、それは print で 出力するものとしてダイレクトに渡される。

引数 how`g'`G' で始まる文字列でもなく、ゼロ以下の 数字であった場合にはただ一度の置換が行われる。how がゼロであった場合、 gawk は警告を発する。

regexptargetにマッチしなければ、gensubの戻り値は 変更されていない元のtargetとなる。

gensubgawkの拡張であり、互換モード(see section コマンドラインオプション)時は 使用することができない。

substr(string, start [, length])

この関数は string 中のstartから始まる 長さ length の部分文字列を返す。文字列の最初のキャラクタは 数値 1 である(35)。たとえば、 substr("washington", 5, 3)"ing" を返す。

lengthが与えられなかった場合、 この関数はstringstart番目から始まる残りの部分全てを返す。 例えば、substr("washington", 5)"ington"を返す。 lengthstart番目から文字列の終端までの長さよりも長い場合も同様である。

startが1よりも小さかった場合、substrはそれを1であったかのように 取り扱う(POSIXはこのような場合について記述していない。Unixのawk はこのように動作するので、gawkもそれに倣っている)。 startが文字列の長さよりも大きかった場合、 substrは空文字列を返す。 同様に、legthが0以下の場合も空文字列を返す。

substrが返す文字列に代入することはできない。 したがって、以下に示す例のようにして文字列の一部を変更しようとすることは間違いである。

 
string = "abcdef"
# "abCDEf" を得ようとしているがうまくいかない
substr(string, 3, 3) = "CDE"
   

また、subgsubの第三引数としてsubstrを使うのも間違いである。

 
gsub(/xyz/, "pdq", substr($0, 5, 20))  # 間違い
   

(一部の商用のawkはこのような使い方のsubstrを許しているが そうすることは移植性を欠くことになる)

もし文字列の一部を置き換えたいのであれば、substrと 文字列連接を以下の例のように組み合わせる。

 
string = "abcdef"
…
string = substr(string, 1, 2) "CDE" substr(string, 6)
   
tolower(string)

この関数は与えられたstring中の大文字を小文字に置き換えた文字列を返す。 このときアルファベット以外のキャラクタは変更されない。 例えば、 tolower("MiXeD cAsE 123")"mixed case 123"を返す。

toupper(string)

この関数は与えられたstring中の小文字を大文字に置き換えた文字列を返す。 このときアルファベット以外のキャラクタは変更されない。 例えば、 toupper("MiXeD cAsE 123")"MIXED CASE 123"を返す。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.3.1 More About `\' and `&' with sub, gsub, and gensub

sub, gsub, gensubを使っているときに バックスラッシュやアンパサンド(`&')を使おうとするときは、 これらの文字が幾つかの段階で エスケープ処理(escape processing) されることに注意する必要がある。

第一に、awkがプログラムを読み込んで内部にプログラム実行のためのコピーを 作るときに字句レベル(lexical level)で。 さらに、awkが生成する置換文字列を実際にスキャンするときに実行時レベル (run-time level)で。

これらの両方のレベルで、 awkはバックスラッシュの後に来ることのできるキャラクタの集合を検索する。 字句レベルでは、エスケープシーケンス に挙げられているようなエスケープシーケンスの検索をする。 したがって、awkが実行時レベルで`\'を処理するには、 字句レベルで二つの`\'をタイプする。 エスケープシーケンスとして正しくないキャラクタが`\'の後に来たときは、 UNIXのawkgawkの両方とも単純に`\'を取り去って、 その後のキャラクタを文字列に送る。 だから、"a\qb""aqb"のように扱われる。

実行時レベルでは幾つかの関数が`\'`&'のシーケンスを違ったやり方で扱う。 この状況は(残念だが)ちょっとばかり複雑である。 伝統的にsubgsubという関数は、`\&' というキャラクタ二つの並びを特別に扱っていてこのシーケンスは一つの`&'に置き換えられたが、 文字列replacement中にある`&'の前にない `\'は変更されずにそのままにされた。 これを表にするとtable-sub-escapesのようになる。

 
 タイプしたもの        sub が見るもの    sub の結果
 -------------        ----------- ----------    -----------------
     \&              &            マッチしたテキスト
    \\&             \&            リテラルの `&'
   \\\&             \&            リテラルの `&'
  \\\\&            \\&            リテラルの `\&'
 \\\\\&            \\&            リテラルの `\&'
\\\\\\&           \\\&            リテラルの `\\&'
    \\q             \q            リテラルの `\q'
   

Table 8.1: 伝統的なsub、gsubでのエスケープシーケンスの処理

この表にはレキシカルレベルの処理で奇数個のバックスラッシュが実行時レベルでは 偶数個になったものと、実行時にsubが処理した結果の両方がある (単純のため、この後の表ではレキシカルレベルで偶数個の`\'がある場合だけを例示する)。

伝統的アプローチに関する問題は、 `\'というリテラルの後にマッチしたテキストが続くというものを取得する手段がない、 ということである。

1992年のPOSIX標準はこの問題を解決した。 この標準では、subgsub`\'の後の`\'にも`&'にも注目するということを述べている。 もし`\'に続いて何かあれば、 そのキャラクタはそのキャラクタそのものとして出力される。 `\'`&'の解釈はtable-sub-posix-92のようになった。

 
 タイプしたもの        sub が見るもの    sub の結果
 -------------        ----------- ----------    -----------------
      &              &            マッチしたテキスト
    \\&             \&            リテラルの `&'
  \\\\&            \\&            リテラルの `\'、続いてマッチしたテキスト
\\\\\\&           \\\&            リテラルの `\&'
   

Table 8.2: 1992年版POSIXによるsubとgsubのエスケープシーケンスの処理の規則

これは問題を解決したかのように見える。 しかし残念ながら、標準で述べていることは一般的ではない。 標準では`\'はその直後にある`\'`&'以外のすべてのキャラクタの特殊な意味を打ち消すと主張しているが、 その特殊な意味は定義されていない。これは二つの問題に繋がる。

table-sub-proposed. 以上の理由により、1996年にgawkのメンテナーは よりオリジナルの実存する練習に近い改定された規則を標準の改訂のために 提案した。提案された規則にはマッチされたテキストの前に`\'が置けるように 特殊なものがある。それはtable-sub-proposedに見ることができる。

 
 タイプしたもの        sub が見るもの    sub の結果
 -------------        ----------- ----------    -----------------
\\\\\\&           \\\&            リテラルの `\&'
  \\\\&            \\&            リテラルの `\', 続いてマッチしたテキスト
    \\&             \&            リテラルの `&'
    \\q             \q            リテラルの `\q'
   \\\\             \\            \\
   

Table 8.3: subとバックスラッシュのために提案された規則

実行時レベルでは、現在三つの特殊なキャラクタ並び、 `\\\&', `\\&', `\&'があるが、 伝統的には一つしかない。しかし、伝統的なケースのように、 これら三つのシーケンスの一部ではない`\'はそのまま出力に現れる。

gawkの3.0と3.1は上記の提案されたPOSXI規則を subgsubに使用している。 POSIX標準は1996年に改訂されることが期待されていた。 2001年の標準は上記の規則に従っていない。その代わりに、 規則が多少単純になった。その結果は一つを除いて同一である。

2001年度版POSIX規則では、置換文字列にある`\&'をリテラルの`&'を 生成するようにし、`\\'はリテラルの`\'を、 その他のキャラクタが後続する`\'をそのまま出力するようにした。 これらの規則はtable-posix-2001-subにある。

 
 タイプしたもの        sub が見るもの    sub の結果
 -------------        ----------------------    -----------------
\\\\\\&           \\\&            リテラルの`\&'
  \\\\&            \\&            リテラルの`\', 続いてマッチしたテキスト
    \\&             \&            リテラルの`&'
    \\q             \q            リテラルの`\q'
   \\\\             \\            \
   

Table 8.4: POIX 2001 でのsubの規則

異なるのは最後の一つである。 `\\\\'`\\'ではなく`\'を生成する。

バージョン 3.1.4から、gawk`--posix'が 指定されたとき(see section コマンドラインオプション)にはPOSIXの規則に従うようになった。 指定されなければここに書いたとおり、 この7年以上にわたり使われてきた1996年提案の規則に従う。

ノート : 次のメジャーリリースでは、gawkはデフォルトで POSIX 2001 のルールを使うようになるだろう。

gensubのルールはかなり単純である。 実行時レベルでは、gawk`\'を見つけると、 その後ろに数字が続いている場合には、 (その数字に対応する)以前に現れているカッコで括られた部分正規表現にマッチしたテキストが 出力テキストに現れる。 それ以外の場合`\'の後ろのキャラクタはなんの意味もなく、 そのキャラクタがそのまま出力テキストに現れるが、`\'は出力されない。 これはtable-gensub-escapesにあるとおりである。

 
 タイプしたもの        sub が見るもの          sub の結果
 -------------        ----------------------          -----------------
      &                    &            マッチしたテキスト
    \\&                   \&            リテラルの `&'
   \\\\                   \\            リテラルの `\'
  \\\\&                  \\&            リテラルの `\'、続いてマッチしたテキスト
\\\\\\&                 \\\&            リテラルの `\&'
    \\q                   \q            リテラルの `q'
   

Table 8.5: gensubのエスケープシーケンス処理

字句レベルの処理や実行時レベルの処理は複雑なので subgsubには特殊ケースがある。 置換を実行するときにはgawkgensubの使用を勧める。

Advanced Notes: Matching the Null String

awkでは、`*'演算子は空文字列にマッチする可能性がある。 これはsubgsubgensubといった関数では重要である。 例を挙げよう。

 
$ echo abc | awk '{ gsub(/m*/, "X"); print }'
-| XaXbXcX
   

この結果は理由が通るものであるが、驚くかもしれない。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.4 Input/Output 関数

以下に挙げる関数は入出力(I/O)に関係するものである。 省略可能な引数はブラケット([ ])で囲まれている。

close(filename [, how])

入力や出力のためのファイルfilenameをクローズする。 この引数は出力のパイプへの、 あるいはパイプからのリダイレクトのシェルコマンドであっても良い。 その場合にはパイプがクローズされる。 詳細はSee section 入出力のリダイレクトをクローズする

コプロセスをクローズするとき、最初に双方向パイプの一方の端点を クローズし、その後でもう一方をクローズするのが便利である。 これはcloseに第二引数を与えることで行うことができる。 この第二引数には文字列"to""from"を指定して パイプのどちらの端点をクローズするかを指示する。 文字列の大小文字の違いは無視される。 この機能に関する詳細な説明と使用例はSee section 別のプロセスとの双方向通信

fflush([filename])

Flush any buffered output associated with filename, which is either a file opened for writing or a shell command for redirecting output to a pipe or coprocess. filenameに結び付けられているオープンしたファイルの出力や、 パイプ、コプロセスにリダイレクト出力しているシェルコマンドのための出力バッファをフラッシュする。

多くのユーティリティプログラムがその出力をバッファリングしている。 これは、ディスク上のファイルやターミナルに書き出す情報をメモリ上に貯えておき、 (送るのに)十分な量になったところで出力デバイスにまとめて送るものである。 しかし、時としてバッファがいっぱいになっていなくても、 情報をその出力先に出力するために強制的にプログラムにそのバッファを フラッシュ (flush)させる必要があることがある。 これが、fflushの目的である。 gawkもまたその出力をバッファリングしており、 fflush関数はgawkのバッファを 強制的にフラッシュさせるために使うことができる。

fflushは1994年にベル研究所で開発されたawkで追加された。 これはPOSIXの標準ではなく、 `--posix'がコマンドラインで指定された場合 (see section コマンドラインオプション)は使用できない。

gawkfflush関数を二つの方法で拡張した。 一つは引数なしの場合を認めたというものであり、 この場合は標準出力のバッファがフラッシュされる。 もう一つは引数として空文字列("") を認めたというものであり、 これはすべてのオープンされているファイル、パイプをフラッシュする。

fflushはバッファのフラッシュに成功した場合は0を返し、それ以外では -1を返す。 すべてのバッファをフラッシュする場合には、 すべてのバッファのフラッシュに成功したときに0を返す。 そうでない場合には-1を返し、 gawkは問題のあったfilenameに関して警告する。

gawkは(getlineを使うなどして) 読み込みのためにオープンされたファイルやパイプを フラッシュしようとしたときや、 filenameがオープンされたファイル、パイプ、コプロセスでないとき にも警告を行う。 このような場合、fflushは-1を返す。

system(command)

この関数はユーザーがオペレーティングシステムのコマンドを実行し、 その後でawkプログラムに戻るということをできるようにする。 関数systemcommandで渡された文字列をコマンドとして実行する。 コマンドから戻ってきたとき、実行したコマンドの終了ステータスを関数の値として返す。

例えば次のコード片をawkプログラムに埋めこむと、

 
END {
     system("date | mail -s 'awk run done' root")
}
   

システム管理者はこのawkプログラムが入力を処理し終わって 入力終了処理が始まったときにメイルを受け取ることになる。

パイプに対してprintprintfをリダイレクトすることは ほとんどの場合仕事を達成するのに十分である。 多くのコマンドを実行する必要があるのなら、 シェルに対するパイプに対して単純に出力してやることで効率良く行うことができるだろう:

 
while (more stuff to do)
    print command | "/bin/sh"
close("/bin/sh")
   

しかしながら、awkプログラムが対話的なものであったときには、 systemはシェルやエディタのような大きな self-containedプログラムを用意するのに便利である。 一部のオペレーティングシステムではsystem関数を実装することができない。 サポートされていない場合にはsystemは致命的エラーを引き起こす。

Advanced Notes: Interactive Versus Noninteractive Buffering

バッファリングというものは、あなたのプログラムが対話的(interactive) であろうがなかろうが、根の深い混乱を招く可能性がある。 たとえば、キーボードの前に坐っているユーザーとのやり取りを考えてみよう。 (37)

対話的なプログラムは一般的にはその出力は行バッファ(line buffer)、 つまり各行毎に出力を行うものとなっている。 対話的でないプログラムはバッファがいっぱいになるまで実際に出力するのを待ち、 複数行がバッファに溜まっているということもある。 以下に挙げるのはその違いを示す例である。

 
$ awk '{ print $1 + $2 }'
1 1
-| 2
2 3
-| 5
Ctrl
-d
   

各行は即座に出力されている。これを以下の例と比較してみよう。

 
$ awk '{ print $1 + $2 }' | cat
1 1
2 3
Ctrl
-d
-| 2
-| 5
   

ここではCtrl -dがタイプされるまで、何も出力されていない。 これはバッファリングされているためであり、 それがcatに対するパイプへ(一度に)送られているからである。

Advanced Notes: systemを使って出力バッファリングを制御する

関数fflushはファイルやパイプに対する出力のバッファリングを 陽に制御する機能を提供する。 しかしこれは、他のawk処理系では使えないので これを使うと可搬性(portability)に欠けることになる。 出力バッファをフラッシュするための手段として、 system関数を空文字列を引数にして呼び出すというものがある。

 
system("")   # 出力をフラッシュする
   

gawkはこのようなsystem関数の使い方を特殊なものとみなし、 シェル(あるいは他のコマンドインタープリター) を何のコマンドもなしに起動するようなことはしない。 このため、gawkではこの使い方は便利であるばかりでなく、効率も良い。 このやり方は他のawk処理系でも使えることが多いので、 必要ないシェルを起動してしまうことを避ける必要もない (他の処理系では、標準出力のバッファだけがフラッシュされ、 その他の出力バッファはフラッシュされないかもしれない)。

もし、プログラマーが期待していることを考えているのなら、 systemがすべてのペンディングされている出力を フラッシュするだろうことが役に立つだろう。

 
BEGIN {
     print "first print"
     system("echo system echo")
     print "second print"
}
   

このプログラムは

 
first print
system echo
second print
   

上記のような出力をするはずで、下に示すようなものではない。

 
system echo
first print
second print
   

もしawksystemを実行する前にバッファをフラッシュしなければ、 後者のような(期待しない)出力がでてくることになる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.5 Using gawk's Timestamp 関数

awkプログラムの common useは、 特定のレコードが記録された時刻を示すタイムスタンプ情報からなる ログファイルに対する処理である。 多くのプログラムは、タイムスタンプをtime というシステムコールが返すある特定の時点(epoch)からの経過秒数の形で記録する。 POSIX システム上では、これは UTCの1970年1月1日の午前零時からの経過秒数である。 ここで、うるう秒はカウントされない。(38) すべてのPOSIX準拠のシステムは0から2^31 - 1までの範囲のタイムスタンプを サポートしている。これはUTCの2038年1月19日 午前3時14分7秒までの 時間を表すのに十分なものである。 多くのシステムではepoch以前の時刻を表す負のタイムスタンプを含め、 より広い範囲のタイムスタンプをサポートしている。

ログファイルの処理や便利なレポートの生成を簡単にするために、 gawkはタイムスタンプを扱う以下のような関数群を提供している。 これらはgawkの拡張であり、POSIX標準にもgawk以外の 知られているawkにもない。(39) 省略可能な引数はブラケット([ ])で囲まれている。

systime()

この関数はシステムエポックから現在までの時間を秒で返す。 POSIX のシステムでは、これはUTCでの1970年1月1日の0時を起点としている。 これは他のシステムでは違った数字かもしれない。

mktime(datespec)

この関数はdatespecsystimeが返すのと同じ形式のタイムスタンプへと変換する。 これはISO Cの同名の関数と同様のものである。 引数datespec"YYYY MM DD HH MM SS [DST]" という形式の文字列である。 この文字列は六個または七個の数値からなり、それぞれ順番に 世紀を含めた年、1から12までの月、1から31までの日、0から23までの時間、 0から59までの分、0から60までの秒(40)、そして省略可能な夏時間フラグである。

これらの数値の値は範囲に収まっている必要はない。 たとえば時間としての-1は深夜零時の一時間前を意味する。 ゼロ始まりのGregorian calendar (グレゴリオ暦?)が仮定され、 1年の前の年は0年、0年の前の年は-1年とされる。 時刻はその地域のタイムゾーンが仮定される。 夏時間フラグが有効であれば、夏時間が使われていると仮定される。 これがゼロであれば時刻は標準時であると仮定される。もしこれがマイナス (デフォルト)であれば、 mktimeは指定された時間が夏時間であるかどうかを検査する。

datespecが十分な要素を持っていないか、 結果の時刻が表せる範囲を越えている場合にはmktimeは-1を返す。

strftime([format [, timestamp [, utc-flag]]])

この関数は文字列を返す。 この関数は ISO のC標準ライブラリの同名の関数と同じものである。 timestampで特定される時間を文字列にするのに使い、 文字列 formatをベースに文字列化する。 もし utc-flag が存在して、ゼロまたは null ではない場合、値は UTC (Coordinated Universal Time (世界標準時)、かつての GMT やグリニッジ標 準時) としてフォーマットされる。 そうでなくても、値はローカルタイムゾーンにフォーマットされる。 timestampsystimeが返した値と同じ書式である。 引数timestampが省略された場合、 gawkはタイムスタンプとして現在時刻を使用する。 引数formatが省略された場合、 strftime"%a %b %d %H:%M:%S %Z %Y"を使用する。 この書式文字列は dateユーティリティの出力と(ほぼ)同じ出力を生成する (3.0以前のバージョンのgawk は引数formatを省略することはできなかった)。

systime関数はログファイルの日付、時間と、プログラムを実行している時点の日付、 時間とを比較することを可能にする。 特に、ある特定のレコードが記録されてからどれくらいたったのかを求めることが簡単になる。 それはまた、``seconds since the epoch''(エポックからの経過秒) フォーマットを使ってログのレコードを作成することを可能にする。

The mktime function allows you to convert a textual representation of a date and time into a timestamp. This makes it easy to do before/after comparisons of dates and times, particularly when dealing with date and time data coming from an external source, such as a log file.

strftime関数はタイムスタンプを人間が読み易い情報に変換するのを簡単にさせる。 この関数はsprintf関数 (see section 文字列操作関数) に似ていて、 書式指定でないキャラクタはそのまま文字列として返され、 書式指定文字列中で日付や時刻を特定するものがあるとそれを置き換える。

strftimeは1999年のISO C 標準 (41) によって以下に挙げる書式指定子をサポートしている。

%a

実行されているロカールでの曜日の略称。

%A

実行されているロカールでの省略なしの曜日の名称。

%b

実行されているロカールでの、月の省略名称。

%B

実行されているロカールでの、省略なしの月の名前。

%c

実行されているロカールでの``適切な''日付と時刻の表示形式 ("C"ロカールでは`%A %B %d %T %Y')。

%C

世紀。年を100で割ってそれに最も近い小さい整数に丸めたもの。

%d

十進数で表わした月の日数(01から31まで)。

%D

`%m/%d/%y'と等価。

%e

その月での日にちで、一桁の場合にはスペースが詰められる。

%F

`%Y-%m-%d'と等価。 これはISO 8601 の日付書式である。

%g

ISO week numberでの年を100で割ったときの剰余で00から99までの十進数となる。 たとえば1993年1月1日は1992年の第53週である。したがって 年が1993年であってもそのISO week number は1992である。 同様に、1973年12月31日は1974年の第一週である。 したがって、年が1973年であってもそのISO week numberは1974である。

%G

十進数で表したISO week numberの全桁。

%h

`%b'に同じ。

%H

24時間表示での時刻(00から23まで)。

%I

12時間表示での時刻(01から12まで)。

%j

十進数で表わされたその年の初めからの日数(001から366まで)。

%m

十進数で表わされた月(01から12まで)。

%M

十進数で表わされた分(00から59まで)。

%n

改行キャラクタ(ASCIIのLF)。

%p

実行されているロカールでの12時間表示の時刻の午前/午後を表わすもの。

%r

実行されているロカールでの十二時間表示の時間 ("C"ロカールでは`%I:%m:%s %p')。

%R

`%H:%M'に同じ。

%S

十進数で表わされた秒(00から60まで)。

%t

タブキャラクタ。

%T

`%H:%M:%S'に同じ。

%u

月曜日を1としたその週における曜日。1から7までの範囲をとる。

%U

その年の第何週かを示す十進数(その年最初の日曜日が第一週の最初の日である) で、 範囲は00から53。

%V

The week number of the year (the first Monday as the first day of week one) as a decimal number (01--53). The method for determining the week number is as specified by ISO 8601. (To wit: if the week containing January 1 has four or more days in the new year, then it is week one; otherwise it is week 53 of the previous year and the next week is week one.)

%w

曜日を表わす数値。0から6までの範囲で、日曜日が0。

%W

その年の第何週かを示す十進数(その年最初の月曜日が第一週の最初の日である) であり、 範囲は00から53。

%x

実行されているロカールでの``適切な''日付表示 (`"C"'ロカールでは`%A %B %d %Y')。

%X

実行されている環境での``適切な''時間表示 ("C"ロカールでは`%T')。

%y

十進数で表わされた世紀を除いた年(00から99まで)。

%Y

十進数で表わされた世紀を含めた年(1995など)。

%z

+HHMMフォーマットでのタイムゾーンのオフセット (RFC 822/RFC 1036 の日付ヘッダーを生成するのに必要)。

%Z

タイムゾーンの名前かその略称、あるいはタイムゾーンが決定できなければ空。

%Ec %EC %Ex %EX %Ey %EY %Od %Oe %OH
%OI %Om %OM %OS %Ou %OU %OV %Ow %OW %Oy

`%c'`%C'など二番目にくる文字による書式の 代替表現(``Alternate representations'')。 (42) これらの機能はPOSIXのdateユーティリティに従ったものである。

%%

`%'という文字。

変換指定文字が上に挙げたものに含まれていない場合にはその結果は未定義である (43)

形式的に、 ロカール(locale)はプログラムを実行する地理的な場所である。 例えば、アメリカ合衆国では1991年9月4日を略して書き表わす普通のやり方は ``9/4/91''である。ヨーロッパの多くの国ではそれを``4.9.91''のように記述する。 したがって、"US"ロカールにおいては`%x'という指定は `9/4/91'という結果となり、 "EUROPE"ロカールでは `4.9.91'という結果となる。 ISO 標準Cでは大部分のCプログラマーが使うであろう環境での デフォルトのCロカールを定義している。

完全に標準に従っていないシステムでは、gawk は GNU C ライブラ リから strftime のコピーを提供している。 そういったものをgawkのコンパイルのときに使用すれば (see section gawk のインストール)、 次に挙げるような書式指定も行なうことが可能である。

%k

十進数での24時間表記の時間(0--23)。 一桁の数字ではスペースが詰められる。

%l

The hour (12-hour clock) as a decimal number (1--12). Single-digit numbers are padded with a space. 十進数での12時間表記の時間(0--12)。 一桁の数字ではスペースが詰められる。

%s

エポックからの経過秒数を十進のタイムスタンプで表した時間。

Additionally, the alternate representations are recognized but their normal representations are used.

次に挙げるサンプルは、awkによるPOSIXのdateユーティリティの実現である。 通常dateユーティリティは現在の日付と時刻をよく知られた書式で出力する。 しかし、`+'で始まる引数を与えた場合、 dateはその引数の非書式指定部分はそのまま標準出力にコピーし、 現在時刻にしたがって文字列中の書式指定子を解釈する。 例を挙げよう。

 
$ date '+Today is %A, %B %d, %Y.'
-| Today is Thursday, September 14, 2000.
   

以下のプログラムはgawkバージョンのdateユーティリティである。 タイムゾーンがUTCにセットされたかのようにdateを実行させる `-u'オプションを取り扱うために、シェルラッパー(shell wrapper)がある。

 
#! /bin/sh
#
# date --- approximate the P1003.2 'date' command

case $1 in
-u)  TZ=UTC0     # UTC を使う
     export TZ
     shift ;;
esac

gawk 'BEGIN  {
    format = "%a %b %d %H:%M:%S %Z %Y"
    exitval = 0

    if (ARGC > 2)
        exitval = 1
    else if (ARGC == 2) {
        format = ARGV[1]
        if (format ~ /^\+/)
            format = substr(format, 2)   # 先行する + を取り除く
    }
    print strftime(format)
    exit exitval
}' "$@"
   

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.6 gawk のビット操作関数

I can explain it for you, but I can't understand it for you.
Anonymous

The operations are described in table-bitwise-ops. 多くの言語には二つの整数値の間でビットごとの演算を行う能力がある。 ビットごとの演算には一般にAND、OR、XORの三つがある。 この操作は table-bitwise-ops で説明されるようなものである:

 
                ビット演算子
          |  AND  |   OR  |  XOR
          |---+---+---+---+---+---
オペランド| 0 | 1 | 0 | 1 | 0 | 1
----------+---+---+---+---+---+---
    0     | 0   0 | 0   1 | 0   1
    1     | 0   1 | 1   1 | 1   0
   

Table 8.6: ビット操作

見ての通り、AND 操作の結果は両方のビットが 1 のときにだけ 1になる。OR 操作の結果はいずれかのビットが 1 のときに 1 になる。 XOR 捜査の結果はいずれかのビット(ただし両方ともではない)が 1 のとき 1 になる。 次の操作は補数(complement)である。1 の 補数は 0 であり、 0 の補数は 1 である。したがって、この操作は与えられた値のビットを``flips'' する。

最後に二つの演算子、ビットの左シフト、右シフトを挙げよう。 たとえば、`10111001' という文字列を持っていてそれを右に3ビット シフトすると、結果は `00010111' となる。(44) 最初に `10111001' とあって、これを3ビット左へシフトすると その結果は `11001000' となる。gawk は以上述べたような ビット操作を行う組み込み関数を提供している。それは以下のとおり:

and(v1, v2)

v1v2 の間でビットごとの AND をとった値を返す。

or(v1, v2)

v1v2 の間でビットごとの OR をとった値を返す。

xor(v1, v2)

v1v2 の間でビットごとの XOR をとった値を返す。

compl(val)

val のビットごとの補数を返す。

lshift(val, count)

val を左に count ビットシフトした値を返す。

rshift(val, count)

val を右に count ビットシフトした値を返す。

これらすべての関数において、倍精度浮動小数点数は最も広い C の符号なし 整数型に変換され、それからビットごとの演算が行われる。もしその結果が C の doubleで表現できないものであれば、先行するゼロでないビットは 正確に表現できるところまで一つ一つ取り除かれる。その後の結果は C の doubleへと再度変換される(もしこのパラグラフで言っていることが わからなくても心配することはない)。

以下に挙げたのはこれらの関数を使ったユーザー定義関数 (see section ユーザー定義関数)である:

 
# bits2str --- バイトデータを 1 と 0 の並びにする

function bits2str(bits,        data, mask)
{
    if (bits == 0)
        return "0"

    mask = 1
    for (; bits != 0; bits = rshift(bits, 1))
        data = (and(bits, mask) ? "1" : "0") data

    while ((length(data) % 8) != 0)
        data = "0" data

    return data
}


BEGIN {
    printf "123 = %s\n", bits2str(123)
    printf "0123 = %s\n", bits2str(0123)
    printf "0x99 = %s\n", bits2str(0x99)
    comp = compl(0x99)
    printf "compl(0x99) = %#x = %s\n", comp, bits2str(comp)
    shift = lshift(0x99, 2)
    printf "lshift(0x99, 2) = %#x = %s\n", shift, bits2str(shift)
    shift = rshift(0x99, 2)
    printf "rshift(0x99, 2) = %#x = %s\n", shift, bits2str(shift)
}
   

このプログラムは実行すると以下のような出力をする:

 
$ gawk -f testbits.awk
-| 123 = 01111011
-| 0123 = 01010011
-| 0x99 = 10011001
-| compl(0x99) = 0xffffff66 = 11111111111111111111111101100110
-| lshift(0x99, 2) = 0x264 = 0000001001100100
-| rshift(0x99, 2) = 0x26 = 00100110
   

bits2str 関数は二進数値を文字列に変換する。 数値の 1 は 最も右側のビットが 1 にセットされている二進数値を 表している。このマスクを使うことによって、この関数は繰り返し最も 右側のビットをチェックしている。マスクと値とを AND することによって 最も右側のビットが 1 であるかそうでないかがわかる。もし 1 であれば "1" が文字列の先頭に連結され、そうでなければ "0" が 連結される。値はその後で右へ1ビットシフトされ、1となっているビットが なくなるまでループを継続する。

初期値がゼロであった場合には単純に "0" を返す。 そうでなければ、最終段階で 8ビットごとの繰り返しになるように ゼロを付加する。これは近代的なコンピュータでは典型的なことである。

BEGIN ルール中のメインコードは十進と八進の間で同じ数字で異なる ものとなることを示し(see section 八進数字および十六進数字)、それから compl 関数、lshift 関数、rshift 関数の結果を例示している。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.7 gawkの文字列翻訳関数を使う

gawkawkプログラムを国際化するための機能を提供している。 以下のリストにある関数群はここでは簡単に説明する。 詳しい説明はSee section gawkの国際化。 省略可能な引数はブラケット([ ])で囲まれている。

dcgettext(string [, domain [, category]])

この関数はロカールカテゴリcategoryのためのテキストドメインdomain の中にあるstringの訳語を返す。 domainのデフォルト値はTEXTDOMAINのその時点での値である。 categoryのデフォルト値は"LC_MESSAGES"である。

dcngettext(string1, string2, number [, domain [, category]])

この関数はロカールカテゴリcategoryのためのテキストドメインdomain にあるstring1string2の訳語で使われるnumberの複数形を返す。 string1はメッセージの英語での単数形で、 string2は同じメッセージの複数形である。 domainのデフォルト値はTEXTDOMAINのその時点での値である。 categoryのデフォルト値は"LC_MESSAGES"である。

bindtextdomain(directory [, domain])

この関数はgawkがメッセージ翻訳ファイルを探すディレクトリを 指定することを可能にする。これはそれらのファイルが標準でない場所に 置くことができないとき(例えばテストの間など)に使われる。 domainが``束縛されている''ディレクトリを返す。

デフォルトのdomainTEXTDOMAINの値である。 directoryが空文字列("")であった場合には bindtextdomainは与えられたdomainのための カレントバインディングを返す。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.2 ユーザー定義関数

複雑なawkプログラムは、 しばしばユーザー定義関数を使うことで単純にすることが可能である。 ユーザー定義関数は組込みの関数 (see section 関数呼び出し)と同じように使うことができるが、 (ユーザー定義関数は)awkに対して関数が行なうことを教えるために、 ユーザーが定義しなければならない。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.2.1 関数定義の構文

関数定義はawkプログラムのルールの中ならどこにでも記述できる。 だから、 awkプログラムの一般的な形というのは、 ルールとユーザー定義の並びと言える。 awkでは関数は使用する前に定義する必要はない。 これは、awkが実行に移る前にプログラムすべてを読み込むからである。

nameと言う名前の関数を定義するには次のようにする。

 
function name(parameter-list)
{
     body-of-function
}
   

nameは定義する関数の名前である。 関数名として許されるのは変数名と同じである。 文字、数字またはアンダースコアの並びで、ただし先頭に数字が来てはいけない。 一つのawkプログラム中ではある名前は、関数名、配列名、 変数名のどれか一つでのみ使用できる。

parameter-listはカンマで区切られた関数の引数と (関数内の)ローカル変数の名前のリストである。 関数が呼ばれるときに引数の名前は、対応する呼びだし側の変数の値を得る為に使われる。 ローカル変数は空文字列に初期化される。 関数は同じ名前を持った二つのパラメータを持つことはできないし、 関数名と同じパラメータを持つこともできない。

ここは関数定義で一番重要な部分である。 なぜなら、関数がどのように動作するかということを記述する部分であるからである。 引数名は関数本体で(その関数に対する)引数を扱うためにある。 ローカル変数は関数本体で一時的な値を扱うための場所を確保する。

仮引数名は文法的にはローカル変数名となんら変わる所はない。 その代り、実引数の値が三つ与えられたとすると、 parameter-listの最初の三つが仮引数となり、後の残りはローカル変数となる。

(関数定義部での)引数の数は、その関数が呼ばれるときの引数とは必ずしも同じ数ではなく、 (そういった関数では)parameter-listの名前は本当の引数と、 ローカル変数がリスト中にある、あるいは省略された引数はデフォルトの値 (空文字列) がセットされている状態で関数呼び出しが行われていると考えられる。

通常関数を記述しようとするときには、引数としていくつ、 ローカル変数としていくつ名前を使うか分かっている。 引数とローカル変数の間には余計にスペースを入れるという取り決めになっている。 そうすることによって、自分以外の人が記述された関数をどのように使うのか理解できる。

関数本体を実行している間、引数とローカル変数は(プログラム中に存在していれば) 同じ名前の変数を関数本体から見えないようにする。 見えなくなった変数は関数の中でアクセスすることはできない。 なぜならローカル変数によって、名前によってその (隠された)変数にアクセスすることができなくなっているからである。 awkプログラム中の他の(隠されていない)変数は、 関数の中でも通常通りに参照したり変更したりすることができる

引数とローカル変数は関数の本体を実行している間だけ存在している。 関数本体から抜けると、隠されていた変数に再びアクセスできるようになる。

関数本体には関数を呼び出す式を含めることができる。 そこでは直接、あるいは他の関数を通して自分自身を呼び出すことができる。 このような場合、関数が再帰的(recrusive)であるという。

gawkを含めた多くのawk処理系では functionというキーワードを funcと省略して使うことができる。 しかし、POSIX では function の使用しか規定していない。 これはある程度実用的な implications である。 gawk*は POSIX 互換モードで動作しているときには (see section コマンドラインオプション)、次のような文で関数定義を行うことができない

 
func foo() { a = sqrt($1) ; print a }
   

これは個々のレコードで関数`foo'の戻り値と共に変数`func'(の内容)を連接し、 その結果を基として対応するアクションを実行するルールとして定義される このことはおそらく望んだところではない (awkはこの入力を文法的に正しいものとして受け入れる。 したがって、関数はawkプログラムの中で定義される前に使われることになるだろう)。

作成したawkプログラムの移植性を保証するためには、 関数を定義するときには常にfunctionキーワードを使用する。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.2.2 関数定義の例

次にユーザー定義関数の例を挙げる。 数値を引数に取りそれを特定の書式で出力するmyprintという関数を呼びだす。

 
function myprint(num)
{
     printf "%6.3g\n", num
}
   

説明のため、myprint関数を使ったルールを挙げる。

 
$3 > 0     { myprint($3) }
   

このプログラムは、先ほど定義した書式にしたがって、 入力されたレコードの三番目のフィールドが正の数であるときに(そのフィールドを)出力する。 というものである。だから入力として次のデータを与えると、

 
 1.2   3.4    5.6   7.8
 9.10 11.12 -13.14 15.16
17.18 19.20  21.22 23.24
   

プログラムの出力はこうなる。

 
   5.6
  21.2
   

次の関数はある配列のすべての要素を削除する。

 
function delarray(a,    i)
{
    for (i in a)
       delete a[i]
}
   

配列を使っているときには、 ある配列のすべての要素を削除して新しいリストの要素で上書きする必要がしばしばある (see section delete)。 それを行うループを操作を行う場所全てで記述する代わりに、 配列をクリアする必要があるところでdelarrayを呼び出せば良い (これは移植性を増す効果がある。`delete array'を配列全体の削除に 使用することは標準でない拡張である)。

次の例は再帰関数のサンプルである。 これは入力として文字列を受け取り、その文字列を逆順にならべた文字列を返す。 再帰関数は再帰を停止する検査を持たなければならない。 この関数の場合、再帰は開始位置がゼロであるとき、 つまり文字列にもうキャラクタが残っていないときである。

 
function rev(str, start)
{
    if (start == 0)
        return ""

    return (substr(str, start, 1) rev(str, start - 1))
}
   

この関数が`rev.awk'という名前のファイルにあったとすると、 このようにテストすることができる。

 
$ echo "Don't Panic!" |
> gawk --source '{ print rev($0, length($0)) }' -f rev.awk
-| !cinaP t'noD
   

Cのctime関数はタイムスタンプを引数にとり よく知られた形式で書式化された文字列を返す。 次に挙げるのは組込み関数のstrftimeを使った awkバージョンのctimeの例である (strftimeの詳細についてはsee section Using gawk's Timestamp 関数)。

 
# ctime.awk
#
# awk version of C ctime(3) function

function ctime(ts,    format)
{
    format = "%a %b %d %H:%M:%S %Z %Y"
    if (ts == 0)
        ts = systime()       # デフォルトとして現在時刻を使う
    return strftime(format, ts)
}
   

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.2.3 ユーザー定義関数の呼び出し

関数呼び出し(calling a function)は関数の実行を引き起こす。 関数呼び出しは一つの式であり、式としての値は関数が返す値である。

関数呼び出しは関数名とそれに続く(括弧に囲まれた)引数である。 引数として式を書くこともできる。 そのような式は呼び出しが実行されたときに評価が行われてその値が実引数として扱われる。 例えば、次のfooという関数の呼び出しでは引数が三つある (最初の一つは文字列連接)。

 
foo(x y, "lose", 4 * z)
   

ノート : 空白(スペースまたはタブ)を関数名と引数リストを囲んでいる左括弧との間に入れてはいけない。 もし間違って空白を入れてしまった場合、 awkは変数と、(括弧に囲まれた) 式との連接であると認識する。 しかし、書かれている名前は変数名ではなく関数名であるから、 エラーが報告される結果となる。

関数が呼ばれるときに関数に対してその実引数の値のコピーが渡される。 これを 値呼び出し(call by value)と言う。 呼びだし側は実引数に式として変数を使うこともできる。 しかし、呼び出される関数はそれを関知せず、単に引数の値があるとだけ認識する。 例えば次のようなコードでは、

 
foo = "bar"
z = myfunc(foo)
   

myfuncの引数として``変数 foo''を渡すのではなく、 (fooの値である)文字列"bar"を渡していると考えた方が良い。 関数myfuncが関数内のローカル変数の値を変更したとしてもそれは (対応している引数も含めて)他の変数には一切影響しない。 myfunc が次のようなものであったとしよう、

 
function myfunc(str)
{
  print str
  str = "zzz"
  print str
}
   

最初の引数のstrを変更しても呼びだし側にあるfooには影響しないmyfuncを呼び出すときのfooの役割はその値である "bar"を計算したときに終わっている。 strmyfuncの外にもあったとしても、 関数の外にある strの値を関数の中で変更することはできない。 これは、myfuncを実行している間は外にある strは隠されているので見ることも変更することもできないからである。

しかし、関数の引数として配列を渡すときは(配列は)コピーされない。 配列の場合には関数内で直接操作できるように配列そのものが渡される。 このような呼び出し形式は通常、参照呼び出し(call by reference)と呼ばれる。 関数内部で( 引数として渡された)配列を変更すると、それは関数の外にも影響する

ノート: 配列引数を関数内部で変更することは 自分が何をしているか、ということがきちんと解っていなければとても危ないといえるだろう。 たとえば、

 
function changeit(array, ind, nvalue)
{
     array[ind] = nvalue
}

BEGIN {
    a[1] = 1; a[2] = 2; a[3] = 3
    changeit(a, 2, "two")
    printf "a[1] = %s, a[2] = %s, a[3] = %s\n",
            a[1], a[2], a[3]
}
   

このプログラムは `a[1] = 1, a[2] = two, a[3] = 3'を出力する。 なぜなら changeit の呼び出しで、aの二番目の要素に "two"を格納しているからである。

一部のawk処理系では定義されていない関数を呼び出すことが許されており、 そしてその問題は実行時に未定義の関数が呼び出されたときにだけ報告されるという動作をしていた。 例えば、

 
BEGIN {
    if (0)
        foo()
    else
        bar()
}
function bar() { … }
# `foo' が定義されていないことに注意
   

`if'文が決して真にならないため、fooが定義されていないという問題は表面にでない。 通常は、プログラムが未定義関数を呼び出すときに問題となる。

`--lint'(see section コマンドラインオプション)が指定されているとき、 gawkは未定義関数の呼び出しを報告する。

一部のawk処理系では、next文 (see section nextをユーザー定義関数の中で使うと実行時エラーとなる。 gawkにはこのような問題はない。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.2.4 return

ユーザー定義関数本体中にreturn文を記述することができる。 この文は関数を呼び出したところへの復帰を行う。 このとき次のようにしてその値を伴って(呼びだし元に)復帰することも可能である。

 
return [expression]
   

expressionは省略可能である。 省略された場合の戻り値は定義されておらず、したがって何が返るか分からない。

全ての関数定義の終わりには戻り値が書かれていないreturn文があるとみなされる。 制御が関数の終わりに達したときに、その関数は予期できない値を返す。 awkはそのような関数の戻り値を使ったとしても警告はしない

値を返すことはしないが何事かを行なうような関数を書きたいと思うこともあるだろう。 そのような関数はCでいうところのvoid関数であり、 Pascalでいうところの手続き(procedure)である。 したがって、何の値も返さないのが妥当であるかもしれない。 そのような関数の返す値を使うのならば、 それにはリスクがあるということを認識しておくのが良いだろう。

次の例は、与えられた配列の要素の中で最大の数値を返すユーザー定義関数である。

 
function maxelt(vec,   i, ret)
{
     for (i in vec) {
          if (ret == "" || vec[i] > ret)
               ret = vec[i]
     }
     return ret
}
   

maxeltの呼び出しは配列の名前をただ一つの引数として行う。 ローカル変数である iretは引数としては扱われない。 maxeltに二つ、もしくは三つの引数を渡すことを止めることはできないが、 それをやった場合の結果は奇妙なものとなるだろう。 関数引数リストのiの前にある余計なスペースは、 iretが引数としては扱われないということを示すものである。 これはあなたが自分のユーザー定義関数を定義するときにも従ったほうが良い約束ごとである。

以下に挙げた例はこのmaxelt関数を使ったもので、 配列をロードしてmaxeltを呼び出す。その後で配列にあった中で最大の数値を報告する。

 
function maxelt(vec,   i, ret)
{
     for (i in vec) {
          if (ret == "" || vec[i] > ret)
               ret = vec[i]
     }
     return ret
}

# 各レコードのすべてのフィールドをnumにロードする
{
     for(i = 1; i <= NF; i++)
          nums[NR, i] = $i
}

END {
     print maxelt(nums)
}
   

入力として以下のデータを与える。

 
 1 5 23 8 16
44 3 5 2 8 26
256 291 1396 2962 100
-6 467 998 1101
99385 11 0 225
   

このプログラムは、配列にある最大値として99385 を報告する。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.2.5 変数の型付けに関する関数とその効果

awk is a very fluid language. awkは非常に流動的な(fluid)言語である。 awkはある識別子が通常の変数なのか配列なのかを 実行時になるまで見分けることができない。 以下はそれを説明する例である。

 
function foo(a)
{
    a[1] = 1   # パラメータは配列
}

BEGIN {
    b = 1
    foo(b)  # 不正: 致命的な型違い

    foo(x)  # x は初期化されていない。動的に配列になる
    x = 1   # これは許されない。実行時エラー
}
   

通常は重大なことではないけれども、知っていて気をつける価値はある。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9. gawkの国際化

その昔、コンピュータメーカーは英語でのみ動作するようにソフトウェアを記述していた。 結局は、ハードウェアベンダーやソフトウェアベンダーは 英語が使われていない国の母国語で彼らのシステムを動作させることができれば そのシステムをもっと売ることができるということに気が付いた。 結局のところ、プログラムやソフトウェアシステムの国際化や地域化は 一般的な習慣になった。

近年まで、国際化に貢献しようとすることは C や C++ で書かれたプログラムには 非常に制限されたものであった。 この 章 では、どうやって gawk が国際化の機能を awk プログラムレベルに対して使用可能にしたかというような、 gawk が国際化のために使っている基礎をなしているライブラリに ついて記述してある。 awk レベルに国際化を可能にさせたことでソフトウェア開発者は さらなる自由度を与え -- それらによってもはや国際化が要求されている 際に C で記述することを必要とされなくなる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.1 国際化と地域化

国際化 はプログラムを再度記述 (または修正) することを意味し、 そうすることで多くのソースコードの変更なくして多国語を使うことが できるようになる。 Localization は特定の言語で動作するように国際化された プログラムに対して必要なデータを供給することを意味する。 おおよそ典型的に、これらの述語はエラーメッセージを表示するのに 使われるような言語、反応を読み取るための言語、数値や通貨の値を 表示したり読むのに関係する情報のような特徴を持つ。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.2 GNU gettext

GNU gettext の機能はメッセージに注目されている; プログラムによって表示される文字列、直接または printf または sprintf を介してフォーマットされるものである。 (45)

GNU gettext の使用の際には、それぞれのアプリケーションが自分の text domain を持っていなければならない。 これは特異な名前であり、`kpilot'`gawk' のようなアプリケーション 別に特定できるものである。 完全なアプリケーションは複数の構成要素を持っている --- C または C++ で 記述されたプログラムがそうであるように、shawk で 書かれたスクリプトも同様である。 構成要素の全てで同じテキストドメインを使うことになる。

議論を形あるものにするには、我々がguide という名前の アプリケーションを書くことを想定してみる。 国際化は以下のようなステップで、この順序で成り立っている:

  1. プログラマは guide の構成要素の全てに対して検討を行って 翻訳に対して候補であるそれぞれの文字列を列挙する。 例えば、```-F': option required'' は良い翻訳候補である。 オプションの名前の文字列を使ったテーブルは異なる (例、ローカル言語が何であれ、gawk`--profile' オプションは同じにしないといけない)。
  2. プログラマは、textdomain 関数を呼び出すことで、 アプリケーションのテキストドメイン ("guide") を gettext ライブラリに教える。
  3. アプリケーションからのメッセージはソースコードから展開されて、 文字列とそれらの翻訳がリストされているポータブルオブジェクトファイル (`guide.po') の中に集められる。 翻訳は最初は空である。 オリジナル (通常は英語) のメッセージは翻訳の検索用のキーとして提供される。
  4. 翻訳者がいるそれぞれの言語に対して、`guide.po' はコピーされて 翻訳は作成され、アプリケーションと一緒に出荷される。
  5. それぞれの言語の `.po' ファイルはバイナリのメッセージオブジェクト (`.mo') ファイルに変換される。 メッセージオブジェクトファイルは実行時に翻訳を高速に検索するような バイナリフォーマットの中にオリジナルのメッセージとそれらの翻訳ファイルを 含んでいる。
  6. guide をビルドしてインストールした際に、バイナリの 翻訳ファイルは標準的な場所にインストールされる。
  7. テストと開発のために、gettextbindtextdomain 関数を 使って標準ではない異なったディレクトリにある `.mo' ファイルを 使うように教えることが可能である。
  8. 実行時には、guidegettext を呼び出して それぞれの文字列を検索する。 戻ってきた文字列は、もし可能であれば翻訳された文字列であり、 不可能であればオリジナルの文字列である。
  9. もし必要であれば、アプリケーションのテキストドメインを 切り替える必要なく、アプリケーションに属するひとつ以上の異なった テキストドメインからメッセージにアクセスできる。

C (または C++) では、文字列のマーキングと動的な翻訳検索は gettext の呼び出しの中でそれぞれの文字列を覆い隠すことで完了する。

 
printf(gettext("Don't Panic!\n"));
   

ソースコードからメッセージを展開するツールは gettext の 呼び出しの中で囲まれた全ての文字列を引き出す。

GNU gettext の開発者は、`gettext' を何度もタイプするのは 苦痛で見るに耐えないと認識して、もっと簡単にするためにマクロ `_' (アンダースコア) を使っている。

 
/* In the standard header file: */
#define _(str) gettext(str)

/* In the program text: */
printf(_("Don't Panic!\n"));
   

これによってタイピングのオーバーヘッドを文字列ごとに、 たった 3 つの特別な文字に還元して、かなり読みやすくなる。 ロカール起因で情報が異なっている型のための ロカール categories がある。 gettext が知っている定められたロカールのカテゴリーは 以下のとおりである:

LC_MESSAGES

テキストメッセージ。これはgetttext操作のデフォルトカテゴリであるが、 必要に応じて別なものに陽に設定される可能性がある(異なるカテゴリを 提供する必要はほとんどない)。

LC_COLLATE

テキスト照合の情報。たとえば、与えられた言語における 異なるキャラクタ/グループの種類がどのように異なっているか。

LC_CTYPE

(see section 正規表現演算子). キャラクタの型情報(アルファベット、数字、大小文字、などなど)。 この情報は/[[:alnum:]]/のような正規表現の POSIXキャラクタクラスを通じてアクセスされる。

LC_MONETARY

通貨記号や、通貨記号が数値の前後どちらかにつくかなどの お金に関する情報。

LC_NUMERIC

小数点や三桁ごとの区切りに使われるキャラクタに関する情報。(46)

LC_RESPONSE

その地域の言語でどのように``yes'' と ``no'' が現れるかを示す 応答情報で、別の情報のように使われる可能性がある。

LC_TIME

12時間制か24時間制か、日付で月を日の前に置くか後に置くか、その地域での月の略称 などの、時間や日付に関する情報。

LC_ALL

上で述べた全部(gettextのコンテキストではあまり便利ではない)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.3 awk プログラムの国際化

gawk は国際化のために以下の変数と関数を提供します。

TEXTDOMAIN

この変数はアプリケーションのテキストドメインを示します。 GNU gettext との互換性のため、デフォルトの値は "messages" です。

_''your message here''

前にアンダースコアを付けた文字定数は実行時の翻訳の候補である。 前にアンダースコアのない文字定数は翻訳されない。

dcgettext(string [, domain [, category]])

この組み込み関数はロカールカテゴリー category 用のテキストドメイン domain の中の string の翻訳を返します。 domain のデフォルトの値は現在の TEXTDOMAIN の値である。 category のデフォルトの値は "LC_MESSAGES" である。

もし category の値を提供するのであれば、 前の セクション に書かれている既知のロケールのカテゴリーのひとつに等しくなければならない。 またテキストドメインを提供しなければならない。 現在のドメインを使うのであれば、TEXTDOMAIN を使いなさい。

Caution: dcgettext 関数の awk 版への引数の 順序は C 版の順序と故意に異なっている。 The awk 版の順序は簡単になるように選んであり、 妥当な awk スタイルのデフォルト引数を許可するように 選ばれてある。

dcngettext(string1, string2, number [, domain [, category]])

この組み込み関数は、ロケールカテゴリー category に対するテキストドメイン domain の中の string1string2 の翻訳の number 用に 使われる複数のフォームを返す。 string1 はメッセージの英語のひとつのバリアントであり、string2 は 同じメッセージの英語の複数のバリアントである。 domain のデフォルトの値は現在の TEXTDOMAIN の値である。 category のデフォルトの値は "LC_MESSAGES" である。

dcgettext 関数用として同じ備考が適用される。

bindtextdomain(directory [, domain])

この組み込み関数によって `.mo' ファイルを探す gettext のディレクトリを特定でき、標準の場所に ない、または置けない場合にも特定できる(例、テストの間)。 domain が ``目指す'' ディレクトリを返す。

デフォルトの domainTEXTDOMAIN の値である。 もし directory が null 文字 ("") の場合、 bindtextdomain は与えられた domain の現在の結合を返す。

awk プログラムでこれらの機能を使うには、 前の セクション 、 に概略が書かれているステップに以下のようにして従う:

  1. 変数 TEXTDOMAIN をプログラムのテキストドメインに設定する。 BEGIN ルールの中が最良であるが (see section 特殊パターン BEGINEND)、 `-v' コマンドラインオプションを解して行うこともできる (see section コマンドラインオプション):
     
    BEGIN {
        TEXTDOMAIN = "guide"
        …
    }
       
  2. 全ての翻訳文字を先頭にアンダースコア (`_') 文字でマークする。 それは文字列の最初のクォート文字と近接しなければならない。 例えば:
     
    print _"hello, world"
    x = _"you goofed"
    printf(_"Number of users is %d\n", nusers)
       
  3. 動的に文字を作成する場合でも、dcgettext 組み込み関数を用いてそれらを翻訳できる:
     
    message = nusers " users logged in"
    message = dcgettext(message, "adminprog")
    print message
       

    ここで、dcgettext の呼び出しにより、どのメッセージを 見つけたかで異なるテキストドメイン ("adminprog") を 提供するが、デフォルトの ``LC_MESSAGES'' カテゴリーを使う。

  4. 開発の間は、テスト用の個人ディレクトリの中に `.mo' ファイルを置いた方が良い。 これは bindtextdomain 組み込み関数で用いられる:
     
    BEGIN {
       TEXTDOMAIN = "guide"   # 我々のテキストドメイン
       if (Testing) {
           # 我々のファイルを見つける場所
           bindtextdomain("testdir")
           # joe is in charge of adminprog
           bindtextdomain("../joe/testdir", "adminprog")
       }
       …
    }
       

See section 単純な国際化の例, 作成するステップを示し、awk から翻訳に用いた例のプログラム。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.4 Translating awk Programs

一度プログラムの翻訳可能な文字列がマークされると、最初の `.po' ファイルを作成するために展開される。 翻訳箇所として、printf が出力する引数の場所がどこかという 順序の再アレンジに有効である。

gawk`--gen-po' コマンドラインオプションが メッセージを展開し、そして次の議論になる。 その後、実行時の printf の順序を再アレンジに対する printf の可能性を取り上げる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.4.1 印付けされた文字列の抽出

一度 awk が動作すると、全ての文字列はマークされ、 テキストドメインを設定して (そうして多分目指していき)、 翻訳を生成する出番となる。 最初に、最初の `.po' を作成するために `--gen-po' コマンドラインオプションを用いる。

 
$ gawk --gen-po -f guide.awk > guide.po
   

`--gen-po' を付けて実行した際に、gawk はプログラムを 実行しません。 その代わり、普通に解析を行い、GNU gettext ポータブルオブジェクトファイルのフォーマットとして標準出力に全ての マークされた文字列を表示す。 また、出力の中には、dcgettext の最初の引数として、または、 dcngettext の最初と 2 番目の最初の定数の文字列が含まれています。 (47) See section 単純な国際化の例, guide 用の翻訳を作成してテストする一貫したステップの完全なリスト。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.4.2 Rearranging printf Arguments

printfsprintf 用のフォーマット文字列 (see section printfを使って出力を整える) は翻訳用に特別な問題を引き起こす。 以下を参照してください: (48)

 
printf(_"String `%s' has %d characters\n",
          string, length(string)))
   

ここで考えられるドイツ人の翻訳は以下のようなものである:

 
"%d Zeichen lang ist die Zeichenkette `%s'\n"
   

問題は明らかである: フォーマットが指定している順序がオリジナルの ものと異なっているのである。 gettext が実行時に翻訳された文字列を返すことができるにも 関わらず、printf の呼び出しの中では引数の順序を変更することが できない。

この問題を解決するためには、printf フォーマットの指定箇所に、 positional specifier と呼ばれる追加のオプション要素を持たせることになる。 例えば:

 
"%2$d Zeichen lang ist die Zeichenkette `%1$s'\n"
   

ここで、位置の指定箇所はどの位置の引数かを示す整数のカウントと `$' で 構成されている。

 
$ gawk 'BEGIN {
>     string = "Dont Panic"
>     printf _"%2$d characters live in \"%1$s\"\n",
>                         string, length(string)
> }'
-| 10 characters live in "Dont Panic"
   

もし存在するのであれば、位置の指定は、フラグの前やフィールド幅や (または) 精度のフォーマット指定の中で最初に持ってくる。

Positional specifiers can be used with the dynamic field width and precision capability:

 
$ gawk 'BEGIN {
>    printf("%*.*s\n", 10, 20, "hello")
>    printf("%3$*2$.*1$s\n", 20, 10, "hello")
> }'
-|      hello
-|      hello
   

NOTE : 位置の指定と一緒に `*' を用いる際には、`*' を最初に置き、 そして整数の位置を示し、そして `$' を置く。 これは幾分直感的ではない。

gawk does not allow you to mix regular format specifiers and those with positional specifiers in the same string:

 
$ gawk 'BEGIN { printf _"%d %3$s\n", 1, 2, "hi" }'
error--> gawk: cmd. line:1: fatal: must use `count$' on all formats or none
   

NOTE : There are some pathological cases that gawk may fail to diagnose. In such cases, the output may not be what you expect. It's still a bad idea to try mixing them, even if gawk doesn't detect it.

Although positional specifiers can be used directly in awk programs, their primary purpose is to help in producing correct translations of format strings into languages different from the one in which the program is first written.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.4.3 awk Portability Issues

gawk's internationalization features were purposely chosen to have as little impact as possible on the portability of awk programs that use them to other versions of awk. Consider this program:

 
BEGIN {
    TEXTDOMAIN = "guide"
    if (Test_Guide)   # set with -v
        bindtextdomain("/test/guide/messages")
    print _"don't panic!"
}
   

As written, it won't work on other versions of awk. However, it is actually almost portable, requiring very little change:


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.5 単純な国際化の例

Now let's look at a step-by-step example of how to internationalize and localize a simple awk program, using `guide.awk' as our original source:

 
BEGIN {
    TEXTDOMAIN = "guide"
    bindtextdomain(".")  # for testing
    print _"Don't Panic"
    print _"The Answer Is", 42
    print "Pardon me, Zaphod who?"
}
   

Run `gawk --gen-po' to create the `.po' file:

 
$ gawk --gen-po -f guide.awk > guide.po
   

This produces:

 
#: guide.awk:4
msgid "Don't Panic"
msgstr ""

#: guide.awk:5
msgid "The Answer Is"
msgstr ""

   

This original portable object file is saved and reused for each language into which the application is translated. The msgid is the original string and the msgstr is the translation.

NOTE : Strings not marked with a leading underscore do not appear in the `guide.po' file.

Next, the messages must be translated. Here is a translation to a hypothetical dialect of English, called ``Mellow'':(50)

 
$ cp guide.po guide-mellow.po
Add translations to guide-mellow.po …

   

Following are the translations:

 
#: guide.awk:4
msgid "Don't Panic"
msgstr "Hey man, relax!"

#: guide.awk:5
msgid "The Answer Is"
msgstr "Like, the scoop is"

   

The next step is to make the directory to hold the binary message object file and then to create the `guide.mo' file. The directory layout shown here is standard for GNU gettext on GNU/Linux systems. Other versions of gettext may use a different layout:

 
$ mkdir en_US en_US/LC_MESSAGES
   

The msgfmt utility does the conversion from human-readable `.po' file to machine-readable `.mo' file. By default, msgfmt creates a file named `messages'. This file must be renamed and placed in the proper directory so that gawk can find it:

 
$ msgfmt guide-mellow.po
$ mv messages en_US/LC_MESSAGES/guide.mo
   

Finally, we run the program to test it:

 
$ gawk -f guide.awk
-| Hey man, relax!
-| Like, the scoop is 42
-| Pardon me, Zaphod who?
   

If the three replacement functions for dcgettext, dcngettext and bindtextdomain (see section awk Portability Issues) are in a file named `libintl.awk', then we can run `guide.awk' unchanged as follows:

 
$ gawk --posix -f guide.awk -f libintl.awk
-| Don't Panic
-| The Answer Is 42
-| Pardon me, Zaphod who?
   

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.6 gawk Can Speak Your Language

As of バージョン 3.1, gawk itself has been internationalized using the GNU gettext package. (GNU gettext is described in complete detail in GNU gettext tools.) As of this writing, the latest version of GNU gettext is バージョン 0.11.5.

If a translation of gawk's messages exists, then gawk produces usage messages, warnings, and fatal errors in the local language.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10. gawk の高度な機能

Write documentation as if whoever reads it is a violent psychopath who knows where you live.
Steve English, as quoted by Peter Langston

This 章 discusses advanced features in gawk. It's a bit of a ``grab bag'' of items that are otherwise unrelated to each other. First, a command-line option allows gawk to recognize nondecimal numbers in input data, not just in awk programs. Next, two-way I/O, discussed briefly in earlier parts of this Web ページ , is described in full detail, along with the basics of TCP/IP networking and BSD portal files. Finally, gawk can profile an awk program, making it possible to tune it for performance.

この章 では gawkの高度な機能について説明する。 それぞれの機能は他の機能とは関係なく、そういったものを集めた ``grab bag''である。 まずはじめにgawkに入力中に存在する十進でない数値を 認識させるためのコマンドラインオプションについて説明する。 続いて 本 Web ページ のこれより前の部分で簡単に説明を行っている TCP/IP ネットワーキングや BSD ポータルファイルに基づく 双方向入出力(two-way I/O) のより詳しい説明を行う。 最後にパフォーマンスの改良を可能にするために gawkawkプログラムのプロファイル(profile) を取ることができることについて説明する。

新たな組み込み関数を gawk に追加する, discusses the ability to dynamically add new built-in functions to gawk. As this feature is still immature and likely to change, its description is relegated to an appendix. 新たな組み込み関数を gawk に追加するでは gawkに新しい組み込み関数を動的に追加する能力について 説明しているが、 この機能は現状まだ未完成なものであり変更される可能性があるので、 その説明は appendix に格下げされた。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.1 十進でない入力データを許す

`--non-decimal-data'オプションを伴ってgawkを起動した場合、 入力データに十進でない定数を使うことができる:

 
$ echo 0123 123 0x123 |
> gawk --non-decimal-data '{ printf "%d, %d, %d\n",
>                                         $1, $2, $3 }'
-| 83, 123, 291
   

この機能を働かせるために、gawkが データを数値として扱うようにプログラムを記述しなければならない:

 
$ echo 0123 123 0x123 | gawk '{ print $1, $2, $3 }'
-| 0123 123 0x123
   

print文はその式を文字列として取り扱う。フィールドは 必要に応じて数値として振舞うことができるにもかかわらずそれらは 文字列であるので、printはそれらを数値として取り扱おうとは しないのである。数値として取り扱うことを強制するために、ゼロを 加える必要があるだろう。例を挙げよう:

 
$ echo 0123 123 0x123 | gawk --non-decimal-data '
> { print $1, $2, $3
>   print $1 + 0, $2 + 0, $3 + 0 }'
-| 0123 123 0x123
-| 83 123 291
   

十進データの先頭にゼロを置くことは一般的であり、また、この機能を 使うことは驚く結果をもたらす可能性があるのでデフォルトではこの機能は 無効になっている。もしこの機能を使いたいのであれば、陽に要求しなければ ならない。

警告: このオプションの指定はお勧めできない。 これは古いプログラムを非常に悪い形で壊しかねない。 代わりに、データをコンバートするのにstrtonum関数を使う (see section 八進数字および十六進数字)。 これはプログラムを書きやすく、かつ読みやすくし驚くような結果を もたらすことが少なくなる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.2 別のプロセスとの双方向通信

 
From: brennan@whidbey.com (Mike Brennan)
Newsgroups: comp.lang.awk
Subject: Re: Learn the SECRET to Attract Women Easily
Date: 4 Aug 1997 17:34:46 GMT
Message-ID: <5s53rm$eca@news.whidbey.com>

On 3 Aug 1997 13:17:43 GMT, Want More Dates???
<tracy78@kilgrona.com> wrote:
>Learn the SECRET to Attract Women Easily
>
>The SCENT(tm)  Pheromone Sex Attractant For Men to Attract Women

The scent of awk programmers is a lot more attractive to women than
the scent of perl programmers.
--
Mike Brennan
   

双方向通信は別の分割されたプログラムに処理させるデータを送って 結果を読み取るような場合に便利である。 このようなことは一時ファイルを使っていつでも行うことができる。

 
# 処理のためにデータを書き出す
tempfile = ("mydata." PROCINFO["pid"])
while (not done with data)
    print data | ("subprogram > " tempfile)
close("subprogram > " tempfile)

# 結果を読み、実行し終わったら一時ファイルを削除する
while ((getline newdata < tempfile) > 0)
    process newdata appropriately
close(tempfile)
system("rm " tempfile)
   

これは動作するがエレガントではない。 そのほかにも、このやり方は複数のユーザー間で共有できないディレクトリで 実行されるプログラムが必要である。たとえば、`/tmp'はだめである。 別のユーザーが同じ名前の一時ファイルを作成してしまう可能性がある。

gawkのバージョン 3.1から、 他のプロセスに対する双方向パイプをオープンすることが可能となった。 二番目のプロセスはgawkと並行して動作するので コプロセス(coprocess)と呼ばれる。 双方向のコネクションは新たな演算子`|&'によって生成される (これはKorn shell kshから借用した)。(51)

 
do {
    print data |& "subprogram"
    "subprogram" |& getline results
} while (data left to process)
close("subprogram")
   

最初に行われる入出力操作は`|&'演算子を使って (サブプログラムを)実行し、gawkが別プログラムのプロセスに対する 双方向パイプを作成することである。 printprintfによって作成された出力は 起動したプログラムの標準入力に書き込まれる。 そしてそのプログラムの標準出力からの出力は getlineを使ってgawkプログラムから読み出すことができる。 `|'を使って起動したプロセス同様、 サブプログラムはシェルによって起動することのできる 任意のプログラムまたはプログラムのパイプラインとすることができる。

幾つかの気をつけておくべきことがらがある。

It is possible to close just one end of the two-way pipe to a coprocess, by supplying a second argument to the close function of either "to" or "from" (see section 入出力のリダイレクトをクローズする). These strings tell gawk to close the end of the pipe that sends data to the process or the end that reads from it, respectively. サブプロセスに対する双方向パイプの片方の端点だけをクローズすることが 可能である。それにはclose関数に 二番目の引数として"to""from"を渡すという ことを行う(see section 入出力のリダイレクトをクローズする)。 これらの文字列はgawkに対して パイプの (プロセスに対してデータを送るか、データを読み取るかの) いずれかの端点をクローズするように指示する。

This is particularly necessary in order to use the system sort utility as part of a coprocess; sort must read all of its input data before it can produce any output. The sort program does not receive an end-of-file indication until gawk closes the write end of the pipe. この機能はシステムのsortユーティリティを サブプロセスの一部として使用するときに必要である。 sortすべての入力を 出力を生成する前に読み込まねばならない。 sortプログラムは gawkがパイプの書き込み側の端点をクローズしない限り 入力の終端を認識することはできない。

When you have finished writing data to the sort utility, you can close the "to" end of the pipe, and then start reading sorted data via getline. For example: sortユーティリティに対するデータの出力を終えたときに パイプの"to"側の端点をクローズすることができ、 それによりgetlineを通してソートされたデータを 読み込むことができるようになる。 例を挙げよう:

 
BEGIN {
    command = "LC_ALL=C sort"
    n = split("abcdefghijklmnopqrstuvwxyz", a, "")

    for (i = n; i > 0; i--)
        print a[i] |& command
    close(command, "to")

    while ((command |& getline line) > 0)
        print "got", line
    close(command)
}
   

This program writes the letters of the alphabet in reverse order, one per line, down the two-way pipe to sort. It then closes the write end of the pipe, so that sort receives an end-of-file indication. This causes sort to sort the data and write the sorted data back to the gawk program. Once all of the data has been read, gawk terminates the coprocess and exits. このプログラムはアルファベットを一行に一つ 逆順に(zを先頭にaを末尾に) 双方向パイプを通してsortに渡す。 書き込みが終わったところで パイプの書き込み側の端点をクローズしているので sortはそこで入力の終端を認識する。 これによりsortは データのソートを行い、 ソート済みのデータをgawkプログラムに書き戻す。 すべてのデータを読み終わったらgawkは サブプロセスを終了させ自分も終了する。

As a side note, the assignment `LC_ALL=C' in the sort command ensures traditional Unix (ASCII) sorting from sort. ちょっとした注意。 sortで指定している`LC_ALL=C'という代入は 伝統的なUnixにおける(つまりASCII)ソーティングを sortにさせるようにしている。

Beginning with gawk 3.1.2, you may use Pseudo-ttys (ptys) for two-way communication instead of pipes, if your system supports them. This is done on a per-command basis, by setting a special element in the PROCINFO array (see section 情報を伝達する組み込み変数), like so: gawk 3.1.2 から、 擬似tty(Pseudo-ttys: pty)をパイプの代わりに 双方向コミュニケーションのために使うことができるようになった (ただしあなたの使っているシステムが擬似ttyをサポートしていることが前提)。 このコミュニケーションは 特殊配列変数PROCNIFO(see section 情報を伝達する組み込み変数) の要素に設定することにより コマンドごとに行われる:

 
command = "sort -nr"           # コマンド。便利のため変数に格納する
PROCINFO[command, "pty"] = 1   # PROCINFO を更新する
print … |& command       # two-way pipe を開始する
…
   

Using ptys avoids the buffer deadlock issues described earlier, at some loss in performance. If your system does not have ptys, or if all the system's ptys are in use, gawk automatically falls back to using regular pipes. 擬似ttyを使うことにより、 多少のパフォーマンスの低下と引き換えに 先に説明したようなバッファリングに絡むデッドロックを避けることができる。 もしあなたの使っているシステムが擬似ttyを有していないか システム上のすべての擬似ttyが使用中であったならば、 gawkは自動的にフォールバックして 通常のパイプを使うようになる。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.3 Using gawk for Network Programming

EMISTERED: A host is a host from coast to coast,
and no-one can talk to host that's close,
unless the host that isn't close
is busy hung or dead.

In addition to being able to open a two-way pipeline to a coprocess on the same system (see section 別のプロセスとの双方向通信), it is possible to make a two-way connection to another process on another system across an IP networking connection.

You can think of this as just a very long two-way pipeline to a coprocess. The way gawk decides that you want to use TCP/IP networking is by recognizing special ファイル名 s that begin with `/inet/'.

The full syntax of the special ファイル名 is `/inet/protocol/local-port/remote-host/remote-port'. The components are:

protocol

The protocol to use over IP. This must be either `tcp', `udp', or `raw', for a TCP, UDP, or raw IP connection, respectively. The use of TCP is recommended for most applications.

Caution: The use of raw sockets is not currently supported in バージョン 3.1 of gawk.

local-port

The local TCP or UDP port number to use. Use a port number of `0' when you want the system to pick a port. This is what you should do when writing a TCP or UDP client. You may also use a well-known service name, such as `smtp' or `http', in which case gawk attempts to determine the predefined port number using the C getservbyname function.

remote-host

The IP address or fully-qualified domain name of the Internet host to which you want to connect.

remote-port

The TCP or UDP port number to use on the given remote-host. Again, use `0' if you don't care, or else a well-known service name.

Consider the following very simple example:

 
BEGIN {
  Service = "/inet/tcp/0/localhost/daytime"
  Service |& getline
  print $0
  close(Service)
}
   

This program reads the current date and time from the local system's TCP `daytime' server. It then prints the results and closes the connection.

Because this topic is extensive, the use of gawk for TCP/IP programming is documented separately. See TCP/IP Internetworking with gawk, which comes as part of the gawk distribution, for a much more complete introduction and discussion, as well as extensive examples.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.4 Using gawk with BSD Portals

Similar to the `/inet' special files, if gawk is configured with the `--enable-portals' option (see section Unix 上での gawk のコンパイル), then gawk treats files whose pathnames begin with /p as 4.4 BSD-style portals.

When used with the `|&' operator, gawk opens the file for two-way communications. The operating system's portal mechanism then manages creating the process associated with the portal and the corresponding communications with the portal's process.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.5 awk プログラムをプロファイリングする

Beginning with バージョン 3.1 of gawk, you may produce execution traces of your awk programs. This is done with a specially compiled version of gawk, called pgawk (``profiling gawk'').

pgawk is identical in every way to gawk, except that when it has finished running, it creates a profile of your program in a file named `awkprof.out'. Because it is profiling, it also executes up to 45% slower than gawk normally does.

As shown in the following example, the `--profile' option can be used to change the name of the file where pgawk will write the profile:

 
$ pgawk --profile=myprog.prof -f myprog.awk data1 data2
   

In the above example, pgawk places the profile in `myprog.prof' instead of in `awkprof.out'.

Regular gawk also accepts this option. When called with just `--profile', gawk ``pretty prints'' the program into `awkprof.out', without any execution counts. You may supply an option to `--profile' to change the ファイル名 . Here is a sample session showing a simple awk program, its input data, and the results from running pgawk. First, the awk program:

 
BEGIN { print "First BEGIN rule" }

END { print "First END rule" }

/foo/ {
    print "matched /foo/, gosh"
    for (i = 1; i <= 3; i++)
        sing()
}

{
    if (/foo/)
        print "if is true"
    else
        print "else is true"
}

BEGIN { print "Second BEGIN rule" }

END { print "Second END rule" }

function sing(    dummy)
{
    print "I gotta be me!"
}
   

Following is the input data:

 
foo
bar
baz
foo
junk
   

Here is the `awkprof.out' that results from running pgawk on this program and data (this example also illustrates that awk programmers sometimes have to work late):

 
        # gawk profile, created Sun Aug 13 00:00:15 2000

        # BEGIN block(s)

        BEGIN {
     1          print "First BEGIN rule"
     1          print "Second BEGIN rule"
        }

        # Rule(s)

     5  /foo/   { # 2
     2          print "matched /foo/, gosh"
     6          for (i = 1; i <= 3; i++) {
     6                  sing()
                }
        }

     5  {
     5          if (/foo/) { # 2
     2                  print "if is true"
     3          } else {
     3                  print "else is true"
                }
        }

        # END block(s)

        END {
     1          print "First END rule"
     1          print "Second END rule"
        }

        # アルファベット順に並べられた関数

     6  function sing(dummy)
        {
     6          print "I gotta be me!"
        }
   

This example illustrates many of the basic rules for profiling output. The rules are as follows:

The profiled version of your program may not look exactly like what you typed when you wrote it. This is because pgawk creates the profiled version by ``pretty printing'' its internal representation of the program. The advantage to this is that pgawk can produce a standard representation. The disadvantage is that all source-code comments are lost, as are the distinctions among multiple BEGIN and END rules. Also, things such as:

 
/foo/
   

come out as:

 
/foo/   {
    print $0
}
   

which is correct, but possibly surprising.

Besides creating profiles when a program has completed, pgawk can produce a profile while it is running. This is useful if your awk program goes into an infinite loop and you want to see what has been executed. To use this feature, run pgawk in the background:

 
$ pgawk -f myprog &
[1] 13992
   

The shell prints a job number and process ID number; in this case, 13992. Use the kill command to send the USR1 signal to pgawk:

 
$ kill -USR1 13992
   

As usual, the profiled version of the program is written to `awkprof.out', or to a different file if you use the `--profile' option.

Along with the regular profile, as shown earlier, the profile includes a trace of any active functions:

 
# Function Call Stack:

#   3. baz
#   2. bar
#   1. foo
# -- main --
   

You may send pgawk the USR1 signal as many times as you like. Each time, the profile and function call trace are appended to the output profile file.

If you use the HUP signal instead of the USR1 signal, pgawk produces the profile and the function call trace and then exits.

When pgawk runs on MS-DOS or MS-Windows, it uses the INT and QUIT signals for producing the profile and, in the case of the INT signal, pgawk exits. This is because these systems don't support the kill command, so the only signals you can deliver to a program are those generated by the keyboard. The INT signal is generated by the Ctrl -C or Ctrl -BREAK key, while the QUIT signal is generated by the Ctrl -\ key.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11. awkgawk の実行

This 章 covers how to run awk, both POSIX-standard and gawk-specific command-line options, and what awk and gawk do with non-option arguments. It then proceeds to cover how gawk searches for source files, obsolete options and/or features, and known bugs in gawk. This 章 rounds out the discussion of awk as a program and as a language.

While a number of the options and features described here were discussed in passing earlier in the book, this 章 provides the full details.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.1 awkを起動する

awkを実行するには二つの方法がある --- 陽にプログラムを 指定するか、ひとつ以上のプログラムファイルを指定するかである。 以下はこれらのテンプレートである。テンプレートにある[…]に 囲まれた部分は省略可能である:

 
awk [options] -f progfile [--] file …
awk [options] [--] 'program' file   

伝統的な一文字のPOSIXスタイルのオプションとともに、gawkは GNUの長いオプションもサポートしている。

空のプログラムとともにawkを起動することが可能である:

 
awk '' datafile1 datafile2
   

このようなことをすることにはあまり意味がない。 awkは空のプログラムが与えられた場合には単になにもいわずに終了する (d.c.) 。 `--lint'がコマンドラインで指定された場合、 gawkはプログラムが空であることに対して警告を発する。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.2 コマンドラインオプション

オプションはダッシュで始まり一文字のキャラクタが続くものである。 GNUスタイルの長いオプションは二つのダッシュとキーワードから構成される。 このキーワードは省略表記することが可能であり、オプションを特定できる 長さまで省略することが可能である。オプションが引数をとる場合、 そのキーワードは直後に等号(`=')が続き、それに引数が続くか あるいはキーワードとその引数が空白で分割される。値を持った 特定のオプションが二度以上与えられた場合、最後の値が使用される。

gawkに対する長いオプションのそれぞれは対応する POISXスタイルのオプションを持っている。長いオプションと短いオプション はすべてのコンテキストで可換である。 オプションとその意味は以下に挙げる通りである:

-F fs
--field-separator fs

変数FSfsをセットする (see section フィールドがどのように分割されるかを特定する)。

-f source-file
--file source-file

awkプログラムを最初の非オプション引数ではなくsource-file の中から見つけ出すように指示する。

-v var=val
--assign var=val

プログラムが実行される前に変数varに値valを 設定する。その変数の値はBEGINルールの中で使用可能である (see section その他のコマンドライン引数)。

`-v'オプションはひとつの変数の設定しかできないが、違う変数の 設定を行うたびごとに使うことができる: `awk -v foo=1 -v bar=2 …'.

警告: `-v'を使って組み込み変数の値を設定する ことは驚くような結果をもたらす可能性がある。awkは 必要に応じて変数の値をリセットすることがあり、与えた値を無視する ことになる