読者です 読者をやめる 読者になる 読者になる

tohokuaikiのチラシの裏

技術的ネタとか。

Confluenceのプラグイン開発を承ります。ご連絡はこちらのホームページからお願いいたします。

パイプとかリダイレクトとかがちょっと理解した。

いやー、いままでパイプとリダイレクト全然分かってなかったんだよね。
適当に使ってましたけど、シェルのリダイレクトを「こわいものなし」というくらい完全に理解しよう - 檜山正幸のキマイラ飼育記読んでわかったよー。


以下、理解のために色々考えてみたこと。
ちなみに、以下の例で入力ファイルがprototype.jsなのはたまたまその場にあったから。全く深い意味は無い。


UNIXの一般的なShellだと、

となっている。デフォルトで。これは今までも良く聞いてきた。


ちなみに、ファイルディスクリプタってなに?って感じだったのだけど、データを出し入れ運ばせられるホースみたいな機能を持ったものを想像した。


とりあえず、最初にある

command >file 2>&1 と command 2>&1 >fileの挙動の違いについて、

つーのをやってみる。

$grep [ prototype.js > hoge 2>&1
だと、ファイルhogeにエラーが出る(○)


$grep [ prototype.js 2>&1 > hoge
だと、標準出力にエラーがでる(× 想定外。上のと同じようにhogeファイルに出ると期待した)

うん。不思議だ。これを説明してくれるらしい。




で、読んでて、最初にひっかかったのは、

受け手がlessだと分かりにくいので、cat infile | grep [ 2>&1 | cat > outfileとして、ファイルoutfileの中身を見るといいかもしれません。

という記述。なんで、|cat > outfileなんじゃ?

$cat infile | grep [ 2>&1 | cat > outfile

$cat infile | grep [ 2>&1 > outfile
としてもいいんじゃないかなーと思ったらダメだった。outfileにエラーが出るかと思ったら標準出力に出た。その理由が「うーん」ってなったんだけど、その直下に書いてあった。というか、まさにその理由が今回の最初に出てきた例がダメな理由なんだよね。

リダイレクト指定>file 2>&1は、左から右に解釈されます

ということで、上記の場合は最初にリダイレクトでイコールにしておいたのちに、標準にしてもその前で2を標準出力にしているので、意味が無いのだ。これ、

  • リダイレクト=参照

って考えると間違い。

  • リダイレクト=代入

なんだね。その場限りのもの。切ない。


んで、次に思ったのは、「左から右って書いてあるのに、パイプは違うじゃん」ってこと。
全体を読んでると、「パイプしてあるって」言うのは「後ろの方に」書かれているのにもかかわらず、「最初に」考慮しなければいけないこととなる。これって、左から右の原則に従ってないよ。


そこでこう考えた。たとえば、
grep camelize prototype.js
は、2つ目の引数が入力に指定されている。んだけど、これは本来なら
cat prototype.js |grep camelize
なんじゃないの?にも拘らず、前者の書き方が許されるのは
grepは第二引数にきたものをファイルとして認識し、ファイルディスクリプタの0にリダイレクトさせるという処理を最初に行う。
っていう機能があるからだと思う。

つまり、grep使うとき「第二引数はファイルで、これを入力値とします」ってのを念頭において操作するのと同様にパイプによる入出力の設定は先に考えておくべきものなのだと。リダイレクトは左から右の原則に則っている。あぁ、だからいたるところに

# デフォルトの初期化

って書いてるのか。最初からそう書いてあるのはそういうことだったのか!!

パイプはデフォルトの初期化を担える唯一の方法なんだな。grepの第二引数の例をみると唯一じゃないね。だけど、そこでコマンドを区切るっていう機能では唯一だな。(多分)


最後のややこしそうな、
command 3>&1 >/dev/null 2>&3 3>&- | less
もあれこれやってみた。

右のlessを一旦ファイルディスクリプタの3に代入してやって、その隙にファイルディスクリプタの1がless向けだったのを/dev/null向けにしてやる。今、3はlessを持っているので、1が死んでしまっても3を使うことでlessへ渡せる。そこで標準エラーである2に3を代入(リダイレクト)させるとlessにでるわけか。(独り言)

じゃあ、これって、/dev/nullに突っ込んだ後に、復帰させてやるには
command 3>&1 >/dev/null 1>&3 2>&3 3>&- | less
とかするの?
grep camelize prototype.js 3>&1 >/dev/null 1>&3 2>&3 3>&- | less
でやってみた。
おぉ、確かに標準出力に出てる。

じゃあ、>/dev/null に落とした振りして、
grep camelize prototype.js 3>&1 >/dev/null >hoge 2>&3 3>&- | less
ってやってやれば、hogeに結果がたまるんだ。おぉ。確かに。

なるほどー。これ、すげーわかったよ。うなきガスる。