awk で複数ファイルをオープン

ふるさと再訪 - 書評 - ミニマルPerl で以下のような引用があります。

Larry's first thought was "Let's use awk." Unfortunately, the awk of that day
couldn't handle opening and closing of multiple files based on informatin in
the files.

これは小飼さんに直接お会いした時 (何度もお会いしているのですが) にも同じことを言われたのを記憶しています。 gawk ユーザーであれば上の引用は「当時の awk」であって「今の awk」ではないことは自明ですが、少し例を挙げておきたいと思います。

とりあえず、ファイルを 100 個ほど作っておきます。

$ seq 100 | awk '{print $0 > $0 ".txt"}'

次に BEGIN ブロックでオープンしていきましょう。

#! /usr/bin/gawk -f
BEGIN {
    for (i = 1; i <= ARGC - 1; i++) {
        while (getline < ARGV[i] > 0) {
            print $0;
        }
    }
}

gawk の場合には以下のようになります。

$ gawk -f multiple_files.awk *.txt
1
10
100
<snip>
99

問題なく最後まで出力できています。

しかし、nawk では以下のようになります。

$ nawk -f multiple_files.awk *.txt
1
10
100
11
12
13
14
15
16
17
18
19
2
nawk: 20.txt makes too many open files
 source line number 4

これは nawk では故意に close() する必要があるのに対して、gawk はもうこれ以上開けなくなると自動的に close() を行ってくれるからです。 限界は環境によって異なりますが、限界まで開くことは可能です。

また、どの awk でも動作するようにするのであれば、基本的には close() を行う必要があります。

#! /usr/bin/gawk -f
BEGIN {
    for (i = 1; i <= ARGC - 1; i++) {
        while (getline < ARGV[i] > 0) {
            print $0;
        }
        close(ARGV[i]);
    }
}

この場合、nawk でも開くのは 1 つづつですが、同じ動作をすることができます。

$ nawk -f multiple_files.awk *.txt
1
10
100
<snip>
99

もっとも、getline には副作用が大きいので十分に注意する必要があり、getline の問題点にも書かれています。