UNIX/Linux及びシェルにおけるファイルの基本操作とコマンド1では、ファイル内容の閲覧や複数のファイル新規作成方法と編集、ファイルコピー、ファイルの移動、ファイル削除などについて書きましたが、ここではヒアドキュメント、ファイルのリダイレクト、標準入力、標準出力、標準エラー出力とファイルディスクリプタについて取り上げます。
リダイレクト(redirect/redirection)とは[ < ]を使ってファイルからの入力を標準入力としたり、
$ mail foo < lsfile.txt
[ > ]や[ >> ]を使って出力をファイルに書き出すことを指し、
$ ls > lsfile.txt
$ ls >> lsfile.txt
のように書くとlsの出力をlsfile.txtというファイルに書き出しますが、前者[ > ]は、lsfile.txtがなければ新規作成して出力し、既に存在する場合は上書き、後者[ >> ]は、既存のlsfile.txtの末尾に追加、存在しない場合、新規作成されます。
$ sort < lsfile.txt > sortfile.out
のようにリダイレクトで入力と出力を指定するとこの例ではlsfile.txtの内容をソートした結果がsortfile.outに書き出されます。
もしエラーがあれば、上記の例では、標準エラー出力は画面に出力されますが、標準エラー出力もsortfile.outに書き出したい場合には下記のようにします。
$ sort < lsfile.txt > sortfile.out 2>&1
また、sh/bashでは、ファイル入出力関数なるものは存在せず、ファイルI/Oもループと併せてリダイレクトを利用する場合(とファイルをリストとして渡す場合)があります。
UNIX/Linux、シェルでは状況に応じてファイルやコマンド、コマンドラインからの入力等を
標準入力 / stdin / standard input
その結果表示等における(既定では)画面出力を
標準出力 / stdout / standard output
(既定では)画面に出力されるエラー出力を
標準エラー出力 / stderr / standard error output
と呼びますが、前述のリダイレクトは、この入力元や出力先をファイルにする方法とも言えます。
また、これらにはファイルディスクリプタ(ファイル記述子)という0~2の数字が割り当てられています。
標準入力 0
標準出力 1
標準エラー 2
例えば、
> foo ←標準出力をファイルfooに出力する
2 > foo ←標準エラー出力をファイルfooに出力する
2>&1 ←標準エラーを標準出力に出力する
という意味合いになり、更に3つめの例は、
$ bar >> filedayon 2>&1
と記述すると結果的に「barの標準出力と標準エラーを(そのデフォルトの出力先である)画面に出力せずにfiledayonファイルに追加で書き出す」という意味になります。
シェルはいくつかの例外はさておき基本的に左から右に処理されますが、ここでは、このコマンド行全てが1つのコマンドなので、「barがfiledayonにリダイレクトされる際にファイルディスクリプタ2(標準エラー出力)をファイルディスクリプタ1(標準出力)にリダイレクトしなさい」という意味合いになります。
よって「bar」の結果が「filedayon」に送られる時点で標準出力は、デフォルトの出力先である画面ではなく「filedayon」というファイルなので「2>&1」で標準出力にリダイレクトされている標準エラー出力も「filedayon」に書き出される為、画面出力はされないということです。
また、UNIX/Linuxのファイルシステムには/dev/nullというゴミ箱、もっと言えば底なし沼のような便利な空間が(あるというのも表現としては微妙な感も)ありますがシェルでは、リダイレクトとこれらのファイルディスクリプタ、時に/dev/nullを利用すると便利なことが結構あります。
$ find . -name "foo" | xargs grep "bar" > /dev/null 2>&1
$ echo $?
0
これはファイルfooにbarという文字列が存在すれば終了ステータスは0、なければそれ以外を返すというUNIX/Linux、C、bashの性質を利用して終了ステータスの確認[$?]だけをしたいという例ですが、画面表示されるはずの正常な結果と仮にエラーがあったとしても、それらは全て/dev/nullに送られてどこかへ消えてなくなる為、何も表示されずにシェルプロンプトが返ってき(ているので直後でecho $?としてい)ます。
ちなみに直接関係はありませんが、出力の結果をファイルと画面、同時に出力を行うことができるコマンドにteeコマンドがあります。
$ ls | tee file_and_monitor
これを実行するとlsの結果がパイプパイプに渡されteeコマンドによりfile_and_monitorという(任意の)ファイルに書き込まれると同時に画面出力されます。
ちなみにUNIX/Linuxの世界では、何らかの例示をする場合の慣例としては、foo、bar、hoge、hogehogeなどの文字列を使用します。
それにしても > や < があるなら上下とか斜め右下38度とか、北北東から南へ2kmとかはないにしても >> と対になる << があってもいいはずだよね?って疑問に思うのは至極当然でヒアドキュメントと呼ばれるものがそれに当たります。
Windows/Macintosh環境でもPerlなどでCGIスクリプトを書いたことがあればお馴染みかもしれませんが、ヒアドキュメントとは[ <<HERE ](慣例的に全て大文字で書かれHERE以外にもよく利用されるEOFなど)を使い、その対となる「HERE(やEOF)が出現するところまで一行または複数行に渡る文字列をタブやスペース、改行など含めそのまま出力」することを指し、
$ cat > here_test.txt <<HERE
ヒアドキュメントを
書いてみようと
試してみた
HERE
[Ctrl]+[D]
のように書くとHEREの直前の行までの文字列をhere_test.txtというファイルに書き出します。
[ <<HERE ]は、[ <<"HERE" ]とダブルクォートでくくっても可ですが、その場合でも位置を示す終端の[ HERE ]の方はくくりません(末尾のHEREを"HERE"とくくってしまうとクォートを含めた"HERE"はヒアドキュメントの終わりではなく文字列として認識されてしまいます)。
この場合、環境によっては[Ctrl]+[D]を押さなくてもプロンプトに戻る場合もあるかもしれません。
[Ctrl]+[D](大文字のD)は、コマンドライン上でシェルにおける入力の終わりを指示するショートカットキー(として割り当てられていて)キーボードで[Ctrl]キーと[D]キーを同時に押すことを指します(から開始カッコを書いて、次にシー、ティー、アール...と入力していく必要はありません)。