■ はじめに
Linux サーバ上に、中身が存在する以下のようなテキストファイルがあるとします。
# cat file.txt
This is a test file.
この時、以下のようにリダイレクト「> /dev/null 2>&1」を付与してコマンド実行すると「標準出力と標準エラー出力の両方が /dev/null という特殊なブラックホールに吸い込まれて捨てられる」という話を、多くの方が学んだのではないでしょうか。
# cat file.txt > /dev/null 2>&1
#

(生成 AI で作成したイメージ図。「2&&1」となっているところが惜しい)
なぜ「> /dev/null 2>&1」を付与するとブラックホールになるのか、ちゃんと理屈を理解されている方は意外と少ないのではと思います。正直なところ理解しなくても使えれば OK ではあるのですが、本質的なところを理解するのは大切なことですので本記事で整理してみることにしました。
■ 前提知識
< /dev/null について>
「/dev/null」とは、Linux における特殊なファイルです。
具体的には、読み込もうとしても何も返さず、書き込もうとしても書き込んだ内容が必ず捨てられる、という性質を持つファイルになります。
「cat file.txt > /dev/null 2>&1」のコマンドでは、この「書き込もうとしても書き込んだ内容が必ず捨てられる」という性質を利用しております。
<標準入出力について>
標準入出力 (stdio) は、以下の 3 種類が存在します。
- 標準入力 (stdin)
- 標準出力 (stdout)
- 標準エラー出力 (stderr)
また、それぞれ以下の数値に対応します。
- 標準入力
- 0
- 標準出力
- 1
- 標準エラー出力
- 2
正確に言うと、これら数値 "0" "1" "2" は「ファイルディスクリプタ」を表しています。
ファイルディスクリプタについては本記事では説明しませんが、後述の内容にはこの数値が頻繁に出てきますので、「標準出力の数値は "1" で、標準エラー出力の数値は "2" なんだな」ということだけ認識しておいてください。
なお、以降の説明で登場する標準入出力は、「標準出力」と「標準エラー出力」の 2 種類のみです。「標準入力」は出てきません。
■ クイズ
では試しに、実行するコマンドを以下のように一部改変してみると実行結果はどう変わるでしょうか?
Linux の検証環境をお持ちの方は、ご自身の環境で実際にどうなるのか試してみることをお勧めします。
※ 以下では、標準出力であることを分かりやすくするため、「>」を「1>」と明記しております。実際には「1>」を「>」に置き換えても同じ結果になります。
cat file.txt 1> /dev/null 1>&2
cat file.txt 2> /dev/null 1>&2
cat file.txt 2> /dev/null 2>&1
cat file.txt 1>&2 1> /dev/null
cat file.txt 2>&1 1> /dev/null
■ クイズの答え
各コマンドを実行した結果は、以下の通りです。
# cat file.txt 1> /dev/null 1>&2
This is a test file.
# cat file.txt 2> /dev/null 1>&2
#
# cat file.txt 2> /dev/null 2>&1
This is a test file.
# cat file.txt 1>&2 1> /dev/null
#
# cat file.txt 2>&1 1> /dev/null
#
さて、予想通りの結果になりましたでしょうか。
パターンによって、「cat file.txt」で読み込んだ結果がターミナルに出力される場合とされない場合の 2 通りに分かれることが分かります。
出力されない場合は、出力結果が /dev/null に吸い込まれてしまったとご理解ください。
■ 考察
各パターンにおいてなぜこのような結果になるのか、一見難しそうに見えますが、リダイレクトの理屈さえ分かってしまえば、実は非常に簡単です。
それでは理屈の理解のため、冒頭で紹介した以下を例に見ていきましょう。
# cat file.txt > /dev/null 2>&1
#
<ポイントはコマンドを 3 つの部品に分解すること>
このコマンドを以下 3 つの部品に分けて考えると、理解しやすくなります。
- cat file.txt
- > /dev/null
- 2>&1
コマンドを実行した時に、裏でこれらの部品がどのように処理されるのかを見ていきましょう。
まずコマンド実行前の最初の状態として、標準出力と 標準エラー出力の接続先は以下となっていることをご認識ください。どちらもデフォルト設定として「ターミナル」が指定されております。
| 最初の状態 | 接続先 |
|---|---|
| 標準出力 | ターミナル (デフォルト) |
| 標準エラー出力 | ターミナル (デフォルト) |
<最初の部品「> /dev/null」の確認>
いざコマンドを実行した時、「cat file.txt」の実行より先に行われることがあります。
それは「リダイレクトの評価」です。
このリダイレクトの評価は、コマンドに記載された左側から順に行われます。
「cat file.txt」「> /dev/null」「2>&1」の 3 つの中でリダイレクトが行われている部品は「> /dev/null」と「2>&1」の 2 つですね。
そのため、まずは左側にある「> /dev/null」のリダイレクトから先に評価が行われることになります。
では「> /dev/null」は何を意味するでしょうか?
これは「標準出力 の接続先を /dev/null に指定する」ということになります。
※ 「>」は、標準出力のリダイレクトを表します。「1>」と書いても同じです。
つまり、リダイレクトの評価によって、先ほどの表は以下のように更新されます。
| 「> /dev/null」の処理後 | 接続先 |
|---|---|
| 標準出力 | /dev/null |
| 標準エラー出力 | ターミナル (デフォルト) |
<次の部品「2>&1」の確認>
さて「> /dev/null」の次に評価されるリダイレクトは、「2>&1」になります。
では「2>&1」は何を意味するでしょうか?
これは「標準エラー出力の接続先を、標準出力と同じ接続先に指定する」ということになります。
※ 「2>」は、標準エラー出力のリダイレクトを表します。「1>」の時と違って数字の「2」は省略できません。
※ 「&1」は、リダイレクト先を標準出力と同じ指定にすることを表します。仮に「&2」だった場合は、リダイレクト先が標準エラー出力と同じ指定になります。
上の表から分かるように、標準出力の接続先は「/dev/null」になっています。
つまり標準エラー出力が「/dev/null」に指定されますので、先ほどの表は以下のように更新されます。
| 「2>&1」の処理後 | 接続先 |
|---|---|
| 標準出力 | /dev/null |
| 標準エラー出力 | /dev/null |
はい、いかがでしょうか。
標準出力、標準エラー出力の接続先がどちらも /dev/null になりましたね。
これが「> /dev/null 2>&1」のリダイレクトがブラックホールと言われる所以 (ゆえん) です。
<最後の部品「cat file.txt」の確認>
さて、リダイレクトの評価はこれで完了したので、残る部品は「cat file.txt」のみです。
上記の表の状態で「cat file.txt」を実行するとどうなるでしょうか?
「cat file.txt」は存在するファイルなので、読み込み時のエラーは発生せず、実行結果 (ファイルの中身) が標準出力に出力されることになります。
標準出力は「/dev/null」に指定されておりますので、cat コマンドで読み込まれた内容は「/dev/null」に吸い込まれ、最終的に以下の実行結果になる、というわけです。
# cat file.txt > /dev/null 2>&1
#
■ おまけ (パターン 1 の場合の考察)
パターン 1 についても、簡単に見ていきましょう。要領は上記と全く一緒です。
※ 繰り返しになりますが、「1>」は標準出力のリダイレクトを表します。「>」に置き換えても同じです。
# cat file.txt 1> /dev/null 1>&2
This is a test file.
まずコマンドを部品に分解すると、以下 3 つになります。
- cat file.txt
- 1> /dev/null
- 1>&2
分解が出来たら、まずはリダイレクトの評価です。
リダイレクトの評価は左側から順に行われますので、最初は「1> /dev/null」です。
デフォルトの状態は、以下でしたね。
| 最初の状態 | 接続先 |
|---|---|
| 標準出力 | ターミナル (デフォルト) |
| 標準エラー出力 | ターミナル (デフォルト) |
これが「1> /dev/null」の評価によって、以下のように更新されます。
| 「1> /dev/null」の処理後 | 接続先 |
|---|---|
| 標準出力 | /dev/null |
| 標準エラー出力 | ターミナル (デフォルト) |
次は「1>&2」のリダイレクトの評価です。
上記の表は以下のように更新されます。
| 「1>&2」の処理後 | 接続先 |
|---|---|
| 標準出力 | ターミナル |
| 標準エラー出力 | ターミナル (デフォルト) |
標準出力の接続先が「ターミナル」に戻りましたね。つまり最初の状態に戻りました。
では最後に「cat file.txt」の実行になります。
cat コマンドによって読み込まれた file.txt の内容が標準出力に出力されます。
標準出力で指定されているのはターミナルですので、以下の通り実行結果がターミナルに出力されることになります。
# cat file.txt 1> /dev/null 1>&2
This is a test file.
■ おまけ (各パターンでの実行結果まとめ)
以下 2 点を整理した表が以下になります。
- 今回ご紹介した全パターンの実行結果
- 最終的に標準出力/標準エラー出力が「ターミナル」か「/dev/null」のどちらに接続されるのか
これを見ると、標準出力/標準エラー出力が「/dev/null」というブラックホールに吸い込まれて捨てられるのは、赤枠で囲んだ「パターン 0」と「パターン 2」であることが分かりますね。
■ おわりに
今回は Linux コマンドのリダイレクトの仕組みにフォーカスしてみましたが、いかがだったでしょうか。これまでなんとなく「> /dev/null 2>&1」を使ってたよ、という方の理解の助けになれば、これ幸いでございます。
