コマンドラインの標準出力と標準エラー出力をリダイレクトで理解する。
突然ですが、標準出力と標準エラー出力ってわかりますか?
この概念はなかなかピンとくるまで時間がかかるのですが、コマンドラインを操作したり、アプリケーションのログを管理する時に知っておくと便利なので、記事にまとめようと思います。
信じられないかもしれないですが実はコンピュータ(OS)は出力先として二つの出力先をもっています。それが標準出力と標準エラー出力です。特にUNIXコマンドなどでは、コマンドが失敗してエラーとなった場合の出力はエラー出力を使い、lsコマンドのファイル一覧の出力は標準出力に表示されます。画面上はどちらも同じ形で表示されるので初心者の方にはピンとこないかもしれないのですが、標準出力とエラー出力は別々に出力されています。
さすがにここで言っただけでは信じてもらえないかと思うので、実験してみます。
標準出力とエラー出力
実験を行う前に、実験をするためのツールを紹介しないといけないのですが、そのツールというのは「>」(リダイレクト)です。この不等号はシェル(コマンドライン)上で、標準出力されたものをファイルに書き込んでくれます。
例を示すと、リダイレクトを使うと下のlsコマンドの結果をファイルに書き込むことができます。
$ls -ltr
total 32
drwxr-xr-x 5 admin staff 160 Nov 13 08:22 dir1
-rw-r--r-- 1 admin staff 5 Nov 15 09:11 fuga.txt
-rw-r--r-- 1 admin staff 4 Nov 15 09:11 bar.txt
-rw-r--r-- 1 admin staff 25 Nov 15 09:14 HelloWorld.js
-rw-r--r-- 1 admin staff 52 Nov 15 09:19 hoge.txt
-rw-r--r-- 1 admin staff 0 Nov 22 10:09 newfile.txt
実際にファイルに書き込んでみると
$ls -ltr > file_list.txt
$cat file_list.txt
total 32
drwxr-xr-x 5 admin staff 160 Nov 13 08:22 dir1
-rw-r--r-- 1 admin staff 5 Nov 15 09:11 fuga.txt
-rw-r--r-- 1 admin staff 4 Nov 15 09:11 bar.txt
-rw-r--r-- 1 admin staff 25 Nov 15 09:14 HelloWorld.js
-rw-r--r-- 1 admin staff 52 Nov 15 09:19 hoge.txt
-rw-r--r-- 1 admin staff 0 Nov 22 10:09 newfile.txt
-rw-r--r-- 1 admin staff 0 Dec 3 22:57 file_list.txt
file_list.txtのファイルの中身がls -ltrの結果になっています。
ここまででリダイレクトの説明は一区切りして、つぎにわざとエラーを起こしてそれをファイルに書き込んでみたいです。
エラー結果をファイルに書き込む為に下記のように存在しないファイルを引数に与えたコマンドを実行して エラーを起こしてみましょう。
ls -ltr no_exist_file.txt > hoge.txt
$ls -ltr no_exist_file.txt > hoge.txt
ls: no_exist_file.txt: No such file or directory
おっと、lsの失敗結果がhoge.txtに書き込まれるかと思いきや、普通にNo such file or directoryというエラーメッセージが出力されています。
エラーをファイルに書き込めない原因
ここまでの説明で勘の良い方はもしかしたら先のコマンドでエラーがファイルに書き込まれない理由に気づいてるかもしれません。
冒頭でUNIXコマンドの失敗時の出力はエラー出力に吐き出されるということを説明しました。また、今回の例を持ち出す代わりにリダイレクトというツールを持ち出しましたが、リダイレクトはあくまで標準出力をファイルに吐き出すものです。
なので、lsコマンドが失敗した時の出力はエラー出力に吐き出されているのでリダイレクトではそれをファイルに書き出すことができなかった。ということです。
ここまでで、なんとなく標準出力とエラー出力という二つの概念がありそれぞれ別物のようだということはわかったと思いますが、ここからは実際の扱い方をみていきましょう。
まず先の例のようにエラー出力をファイルにリダイレクトしたい場合は「2>」というように書くことでエラー出力をファイルにリダイレクトすることができます。
$ls -ltr no_exist_file.txt 2> hoge.txt
$cat hoge.txt
ls: no_exist_file.txt: No such file or directory
ファイルをcatしていると見事にファイルの中身にエラー出力が書き出されています。さらに標準出力もエラー出力も両方ファイルに書き出す場合は以下のようにします。
$ls -ltr no_exist_file.txt > hoge.txt 2>&1
$cat hoge.txt
ls: no_exist_file.txt: No such file or directory
2>&1がエラー出力を標準出力にながすという意味になります。これはシェルスクリプトを書いたりするとスクリプトの出力はすべてログに吐き出したい!なんて言う時によく使います。また標準出力とエラー出力を別々のファイルに書き出したいという場合は次のようにします。
$ls -ltr no_exist_file.txt 1> hoge.txt 2> hoge.err.txt
この書き方でhoge.txtには標準出力をhoge.err.txtにはエラー出力を書き込むことができます。
まとめ
標準出力とエラー出力についてリダイレクトを使いながら簡単に説明しましたがいかがだったでしょうか?標準出力もエラー出力も一見おなじように画面に出力されるので、最初ピンとこないかもしれませんがリダイレクトなどを勉強してみて実際にコマンドを叩いてみると理解できるようになるのではないでしょうか?
この標準出力を使ったコマンドにはリダイレクトの他にもパイプなどもあり、これらを覚えておくとコマンド操作がされに楽になります。コマンド操作を行う上でこれらの標準出力、エラー出力、標準入力あたりの知識は必須の知識なので必ず覚えておくようにしましょう。
パイプ、リダイレクト、ヒアドキュメントなどは他の言語にはみられないものなので学んでおくと「コマンドラインってこんなことができるんだ!」っていう喜びがあると思います。それぞれについてはまた別の機会に別の記事でまとめます。