Lambdaカクテル

Common Lispと自宅サーバにWebエンジニアリングの香りを載せて

Common Lispのread-lineがパイプ/リダイレクトを受け付けなくて困っている

read-lineとパイプ処理の組合せでつまづいて分からないままの箇所があるので記録する.

ご存知read-lineは,ストリームから1行読み込む重要な関数だ. そしてパイプは,前のコマンドの標準出力を次のコマンドの標準入力に接続させるという,linux/unixを使っていたら避けては通れない重要な機能だ.

この二者を組合せることはよくある典型的な使い方だといえる.試しに,「標準入力からテキストを受け取り,それをただ出力するだけのプログラム」を作成してみる.

read-lineの構文は,(read-line 入力ストリーム 1文字も読まずにeofになったときにエラーにするか eofのときに返す値)である.

(loop for line = (read-line *standard-input* nil nil)
      while line
      do (format t "~A~%" line))

このようなコードになった.eofのときにはnilを返し,while linenilでない間だけ繰り返させる.

そして次のようにも書ける.

(loop for line = (read-line *standard-input* nil :EOF)
      until (eq line :EOF)
      do (format t "~A~%" line))

こちらではeofのときに:EOFキーワードを返させている.そしてuntil (eq line :EOF)で,lineが:EOFにならない限り繰り返させる. キーワードシンボルはeqで比較できるのでeqlなどを使わなくてよい.

この二者は標準入力に直接キーボードで入力しているうちは全く同じように振る舞うのだが,パイプやリダイレクトを使うと全く違う挙動になる. 前者のnilを使うバージョンでは,パイプやリダイレクトを介してテキストを入力しても全くこれを無視し,何も渡さずに起動したときのように振る舞う. 後者の:EOFを使うバージョンでは,パイプやリダイレクトを介してもきちんと動いてくれる.つまりパイプやリダイレクトされていたらこれを入力として読み込み,これを表示する. この違いはどこから来るのだろう?

今回ちょこっと調べてみたが,よく分からなかった.別環境で動作が再現したり,実装の事情を知っている方がおられましたら教えてください.

実行環境

  • roswell
  • sbcl/1.3.19