メモの日々


2017年05月24日(水) [長年日記]

[shell][unix] Bashにおけるリダイレクト

おれはリダイレクトを雰囲気で使っている。たまに混乱するので気になる所を調べてメモしておく。使用するシェルはBashとする。

ファイルディスクリプタ

リダイレクトを理解するにはファイルディスクリプタを理解する必要があるだろう。ファイルディスクリプタをどう表現するのが正しいかよくわからないが、

  • 入出力先を持つ非負の整数

でどうだろうか。入出力先はファイルや画面、キーボードなどになる。

プロセスは生成されたときに0,1,2という3つのファイルディスクリプタを持っている。これらが入出力先として何を持っているのかは親プロセスが決めることであり、プロセス自身は関知しない。

リダイレクトとは

シェルがリダイレクトで行うのは、

  • 新しいファイルディスクリプタを作成しその入出力先を設定する。
  • 既存のファイルディスクリプタの入出力先を変更する。
  • ファイルディスクリプタを破棄する。

だと理解すればいいと思う。

大事な点として、リダイレクトによるファイルディスクリプタの操作は対象とするコマンドに対してのみ行われ、コマンドを実行するシェル自体には影響がないということがある。例えば

$ echo hello >/tmp/oreore

はファイルディスクリプタ1の出力先を変更するが、この変更はechoコマンドのみに作用し、この後に実行するコマンドには影響しない。例外はコマンドが「exec」だった場合で、それについては別にメモする(メモした)。

Bashのリダイレクトの記法はたくさんあるが、以下ではその中のいくつかについてだけメモする。

[n]>word

よく使う、ファイルへのリダイレクトである。[n]にはファイルディスクリプタ(省略すると1になる)、wordにはファイルパスを指定し、これにより次が行われる。

  • ファイルwordがないなら作成する。
  • ファイルディスクリプタ[n]がないなら作成する。
  • ファイルディスクリプタ[n]の出力先としてファイルwordを設定する。

例えば、

$ echo hello 5>/tmp/oreore

を実行すると空の /tmp/oreore ファイルが作られる。echoコマンドはファイルディスクリプタ1へ出力を行うコマンドであり、ファイルディスクリプタ5へは出力を行わないからである。

[n]>&word

>の後ろに&を付けると、wordにはファイルパスではなくファイルディスクリプタか - を指定することになる。wordがファイルディスクリプタの場合は次が行われる。

  • ファイルディスクリプタ[n]がないなら作成する。
  • ファイルディスクリプタ[n]の出力先として「ファイルディスクリプタwordの出力先」を設定する。

wordが - の場合は次が行われる。

  • ファイルディスクリプタ[n]を破棄する。

例えば、

$ echo hello 5>/tmp/oreore >&5

とするとファイルディスクリプタ1の入出力先が /tmp/oreore になるので /tmp/oreore には hello が出力される。あるいは、サブシェルを使って

$ (echo hello >&5) 5>/tmp/oreore

としても同じ結果になる。また、

$ echo hello >&-

とするとファイルディスクリプタ1が破棄されるのでechoコマンドはエラーになる。

[n]>&digit-

似た記法があって、>& の後にファイルディスクリプタdigitと - の両方を指定した場合は次が行われる。

  • ファイルディスクリプタ[n]がないなら作成する。
  • ファイルディスクリプタ[n]の出力先として「ファイルディスクリプタwordの出力先」を設定する。
  • ファイルディスクリプタdigitを破棄する。

ファイルディスクリプタdigitをもう使わない場合はこちらの記法の方がいいのだろうけど、あまり使われない気がする。

ここまでの3種類の記法は > を < に変えるとファイルディスクリプタへ出力先ではなく入力先を設定することになる。出力のリダイレクトにはもう一つ記法があるのでそれもメモしておく。

&>word, >&word

wordにはファイルパスを指定する。これにより次が行われる。

  • ファイルwordがないなら作成する。
  • ファイルディスクリプタ1または2がないなら作成する。
  • ファイルディスクリプタ1と2の出力先としてファイルwordを設定する。

この記法はファイルディスクリプタ1と2の両方をファイルにリダイレクトするときに使う。