知っておいた方がよい I/O 演算子もいくつかあります。バッククォートで括ら れた文字列は、まず、ダブルクォート文字列のように、変数の展開が行なわれま す。その後、シェルでの場合と同じように、コマンドとして解釈され、そのコマ ンドの出力がこの擬似リテラルの値となります。スカラコンテキストでは、出力 すべてを含む一個の文字列が返されます。リストコンテキストでは、出力の 1 行 1 行が個々の要素となるリストが返されます。($/ を設定すれば、行の終わ りを示す文字を変えることができます。)コマンドは、この擬似リテラルが評価 されるごとに実行されます。コマンドのステータス値は $? に返されます ($? の解釈については、See section 定義済み変数,を参照してください)。csh での場合と は違って、結果のデータに対する変換は行なわれず、改行は改行のままです。ど のシェルとも違って、シングルクォートがコマンド中の変数名を解釈させないよ うにすることはありません。シェルに$ を渡すには、バックスラッシュでエスケー プしなければなりません。バッククォートの一般形は、qx// です。
山括弧の中のファイルハンドルを評価すると、そのファイルから、次の行を読み 込むことになります (改行も含まれるので、未定義値が返される EOF に達する までは、偽と評価される値が返ることはありません)。通常は、その値を変数に 代入しなければなりませんが、自動的に代入される場合が 1 つだけあります。 この入力シンボルが、while ループの条件式中に単独で現れた場合だけは、その 値が自動的に変数 $_ に代入されます。(これは、奇妙に思えるかもしれません が、ほとんどすべての Perl スクリプトでこれが必要になることでしょう。)い ずれにせよ、以下のものは、お互いに同値なものです:
while ($_ = <STDIN>) { print; } while (<STDIN>) { print; } for (;<STDIN>;) { print; } print while $_ = <STDIN>; print while <STDIN>;
STDIN、STDOUT、STDERR というファイルハンドルは、あらかじめ定義されていま す。(stdin、stdout、stderr というファイルハンドルも、ローカルな名前でこ れらのグローバルな名前が見えなくなっているパッケージを除けば、使用するこ とができます。)その他のファイルハンドルは、open() 関数で作ることができま す。
<FILEHANDLE> がリストを必要とするコンテキストで用いられると、1 要素に 1 行の入力行すべてからなるリストが返されます。これを使うと簡単に巨大なデー タが作られてしまいますので、注意を要します。
ヌルファイルハンドル <> は特別で、sed や awk の動作をエミュレートするた めに使われます。<> からの入力は、標準入力からか、コマンドライン上に並べ られた個々のファイルから行なわれます。動作の概要は、以下のようになります。 最初に <> が評価されると、配列 @ARGV が調べられ、空であれば、$ARGV[0] に "-"を設定します。これは、open されるとき標準入力となります。その後、 配列 @ARGV がファイル名のリストとして処理されます。
while (<>) { ... # 行ごとの処理 }
というループは、
unshift(@ARGV, '-') if $#ARGV < $[; while ($ARGV = shift) { open(ARGV, $ARGV); while (<ARGV>) { ... # 行ごとの処理 } }
のような Perl の擬似コードと等価です。わずらわしく書かなくても、動作しま す。実際に @ARGV を shift しますし、その時点のファイル名を変数 $ARGV に 入れています。また、内部的にファイルハンドル ARGV を使っていて、<> は不 思議な <ARGV> の同義語となっています。(上記の擬似コードは、<ARGV> を通常 のものとして扱っているので、うまく動作しません。)
終的に、@ARGV に扱いたいと思っているファイル名が含まれるのであれば、最 初に <> を評価する前に @ARGV を変更することも可能です。行番号 ($.) は、 入力ファイルがあたかも 1 つの大きなファイルであるかのように、続けてカウ ントされます。(個々のファイルごとにリセットする方法は、perlfunc manpage の eof() の例を参照してください。)
最初から @ARGV に自分でファイルのリストを設定してもかまいません。スクリ プトにスイッチを渡したいのであれば、Getopts モジュールを使うこともできま すし、実際の処理の前に
while ($_ = $ARGV[0], /^-/) { shift; last if /^--$/; if (/^-D(.*)/) { $debug = $1 } if (/^-v/) { $verbose++ } ... # その他のスイッチ } while (<>) { ... # 個々のファイルに対するコード }
のようなループを置くこともできます。
シンボル <> が「偽」を返すのは一度きりです。偽となったあとで、もう一度呼 び出すと、新たに別の @ARGV を処理するものとみなされ、その時に @ARGV を 設定しなおしていないと、STDIN から読み込むことになります。
山括弧の中の文字列が (<$foo> のような) スカラ変数の参照となっていれば、 その変数が入力を行なうファイルハンドルの名前を示しているとみなされます。
山括弧の中の文字列がファイルハンドルでなければ、グロブを行なうファイル名 のパターンと解釈され、コンテキストによって、ファイル名のリストか、そのリ ストの次のファイル名が返されます。まず、1 段階だけ $ の展開が行なわれま すが、前の段落に書いた間接ファイルハンドルと同じになる、<$foo> のように は書けません。ファイル名グロブと解釈させるには <${foo}> のように中括弧を 入れる必要があります。(別の方法として、glob($foo) と内部関数を呼ぶことも できます。おそらく、まず、こちらの方で試すのが正解でしょう。) 例:
while (<*.c>) { chmod 0644, $_; }
は、
open(FOO, "echo *.c | tr -s ' \t\r\f' '\\012\\012\\012\\012'|"); while (<FOO>) { chop; chmod 0644, $_; }
と等価です。実際、現在はそのようにインプリメントされています。(つまり、 マシン上に csh(1) がないと、スペースを含むファイル名は、うまく扱えないと いうことです。)もちろん、もっと簡単に
chmod 0644, <*.c>;
と書けます。
グロブはシェルを呼び出しますから、自分で readdir() を呼んで、得られたファ イル名に grep() した方が速い場合もあります。さらに、現在のインプリメント がシェルを使うために、(/bin/csh として tcsh(1L) をインストールしていない 限り) glob() ルーティンで "Arg list too long" エラーが発生する場合があり ます。
Go to the first, previous, next, last section, table of contents.