こんにちは、さるまりんです 🐒🔧
いろんな言語のプログラミングやコマンドラインで、ときどき呪文のような書き方に出逢いませんか?
たとえば、Linuxのこんなの。
command > log.txt 2>&1
最初に見たときは、
「なんだこれ……?」
ってなりやすいと思います。
でも、こういう呪文っぽいものの意味がわかると、ちょっと楽しくなってきます。
新しいスキルをひとつ手に入れて、レベルアップした気持ちになります。
魔術師レベルアップです。
今回は、僕にとって長い間使いこなせなかったLinuxでよく出てくる、
- 標準出力
- 標準エラー出力
2>&1
について、できるだけシンプルに整理してみたいと思います。
■ Linuxの出力はひとつではない
Linuxでコマンドを実行すると、画面に結果が表示されます。
たとえば、こんなコマンドです。
ls
ファイルやディレクトリの一覧が表示されます。
一方で、存在しないファイルを指定すると、エラーメッセージが表示されます。
ls no-such-file
たとえば、こんな感じです。
ls: cannot access 'no-such-file': No such file or directory
どちらも画面に表示されるので、最初は同じものに見えます。
でもLinuxの中では、これらは別のものとして扱われています。
通常の結果は標準出力。
エラーメッセージは標準エラー出力。
という別の出口から出ています。
■ 標準出力とは
標準出力は、コマンドの普通の結果が出る場所です。
英語では stdout と呼ばれます。
たとえば、次のようなコマンドの結果です。
ls
echo "hello"
date
これらは、エラーではなく「コマンドが普通に出した結果」です。
C++で言うと、cout のストリームに近いものですね。
■ 標準エラー出力とは
標準エラー出力は、エラーメッセージが出る場所です。
英語では stderr と呼ばれます。
たとえば、存在しないファイルを見ようとした場合です。
ls no-such-file
このとき表示されるエラーメッセージは、標準出力ではなく、標準エラー出力に出ます。
画面に表示されるだけだと違いが見えにくいのですが、Linuxとしては別の出口から出している、ということです。
こっちはC++で言うと、cerr に近いものです。
■ 数字で見ると少しわかりやすい
Linuxでは、標準出力や標準エラー出力に番号がついています。
| 番号 | 名前 | 意味 |
|---|---|---|
| 0 | 標準入力 | 入力 |
| 1 | 標準出力 | 普通の結果 |
| 2 | 標準エラー出力 | エラー |
今回大事なのは、1 と 2 です。
1 が標準出力。
2 が標準エラー出力。
つまり、2>&1 の 2 は、標準エラー出力を指しています。
少し呪文がほどけてきました。
■ > は標準出力をファイルに書く
まずは、普通のリダイレクトから見てみます。
ls > out.log
これは、ls の結果を画面に出さず、out.log に書き込むという意味です。
> は標準出力をファイルへ送ります。
つまり、これは本当はこういう意味に近いです。
ls 1> out.log
1 は省略できるので、普段は書かないことが多いです。
■ エラーは > だけではファイルに入らない
では、エラーが出るコマンドを試してみます。
ls no-such-file > out.log
この場合、エラーメッセージは画面に表示されます。
out.log には入りません。
なぜかというと、> が送っているのは標準出力だけだからです。
エラーメッセージは標準エラー出力なので、別の出口から出ています。
ここが大事なところです。
画面に出ているものが、すべて標準出力とは限りません。
■ 2> は標準エラー出力をファイルに書く
標準エラー出力をファイルに書きたいときは、2> を使います。
ls no-such-file 2> error.log
これで、エラーメッセージが error.log に書き込まれます。
普通の結果をファイルに出したいときは >。
エラーをファイルに出したいときは 2>。
2 は、さっきの標準出力のようには省略できないので、ちゃんと書きます。
ここまでわかると、呪文の中身が見やすくなってきたんじゃないでしょうか。
■ 標準出力と標準エラー出力を分けて保存する
標準出力と標準エラー出力は、別々のファイルに保存することもできます。
command > out.log 2> error.log
これは、
- 普通の結果は
out.log - エラーは
error.log
に書き込む、という意味です。
ログを分けて見たいときには便利です。
たとえば、正常な処理結果とエラーだけを分けて見たい場合です。
■ では 2>&1 は何をしているのか
いよいよです。全部合わせてみましょう。
command > log.txt 2>&1
この 2>&1 は、
標準エラー出力を、標準出力と同じ場所へ送る
という意味です。
もう少し分けて見ると、こうです。
command > log.txt
まず、標準出力を log.txt に送ります。
そのあとで、
2>&1
標準エラー出力も、標準出力と同じ場所へ送ります。
この場合、標準出力はすでに log.txt に向いています。
なので、標準エラー出力も log.txt に入ります。
つまり、
command > log.txt 2>&1
は、
普通の結果もエラーも、まとめて log.txt に書き出す
という意味になります。
■ 順番には少し注意が必要
ここで少しだけ注意があります。
リダイレクトは、書いた順番に処理されます。
そのため、次の2つは同じ意味ではありません。
command > log.txt 2>&1
command 2>&1 > log.txt
1つ目は、標準出力を log.txt に向けたあとで、標準エラー出力も同じ場所へ送ります。
なので、普通の結果もエラーも log.txt に入ります。
一方、2つ目は先に標準エラー出力を「その時点の標準出力」と同じ場所へ送っています。
その時点では、標準出力はまだ画面です。
そのあとで標準出力だけを log.txt に向けるので、エラーは画面に残ることがあります。
このあたりはちょっとややこしいですね。
なので、まずは、
command > log.txt 2>&1
をひとつの形として覚えておくのが良いかもしれません。
呪文のまま?
■ 最近は &> という書き方もある
bashでは、標準出力と標準エラー出力をまとめてファイルに送る書き方として、次のようなものもあります。
command &> log.txt
これは、標準出力と標準エラー出力をまとめて log.txt に書き込む書き方です。
ただし、&> はbashで使える書き方なので、すべてのshellで同じように使えるとは限りません。
その意味でも、2>&1 の意味を知っておくと、ログやシェルスクリプトを読んだときに理解しやすいと思います。
なので、まずはこっちを覚えましょう。
command > log.txt 2>&1
この意味を分解して見るのが理解につながると思います。
■ どんなときに使うのか
2>&1 は、ログをまとめて残したいときによく使います。
たとえば、こんな場面です。
./batch.sh > batch.log 2>&1
バッチ処理の結果もエラーも、まとめて batch.log に残す形です。
cronで実行するスクリプトでも見かけることがあります。
0 3 * * * /path/to/script.sh > /path/to/script.log 2>&1
夜中に動く処理などでは、画面を見ている人がいません。
なので、あとから読めるようにログへ残しておくことが大事です。
■ まとめるか、分けるか
標準出力と標準エラー出力は、まとめることも分けることもできます。
まとめて見たいなら、こう。
command > log.txt 2>&1
分けて見たいなら、こう。
command > out.log 2> error.log
どちらが正解ということではなくて、どう使いたいかで考えるものだと思います。
小さな確認なら、まとめて1つのログでも十分です。
でも、エラーだけを追いたい場合は、分けた方が見やすいこともありますね。
■ さいごに
2>&1 は、最初に見るとかなり呪文っぽいです。
でも分解してみると、
1は標準出力2は標準エラー出力>は出力先を変える2>&1は標準エラー出力を標準出力と同じ場所へ送る
という意味になります。
プログラミングやLinuxには、こういう呪文のような書き方がいろいろあります。
でも、ひとつ意味がわかると、少し見える世界が広がります。
「なんとなくコピペしていたもの」が、自分で読めるものに変わると嬉しくないですか?
こんなちょっとした理解の積み重ねが、モノを作ることの楽しみだなって思っています。
読んでくださってありがとうございました。
それではまた!