いい加減覚えよう。 `command > /dev/null 2>&1`の意味


## 質問

- `command > dev/null 2>&1`ってよく書いてませんか？
- crontabでよく書いてませんか？
- でもそれ、前のcrontabコピペして書いてませんか？
- 実は意味を理解せずに書いてませんか？

## ゴール

- ファイルディスクリプタを理解する
- `command > /dev/null 2>&1`の意味をきちんと理解する

## 基礎知識

### 1. ファイルディスクリプタ

Unixには、次の3つの入出力があり、それぞれ番号が振られています。

- 0: 標準入力
- 1: 標準出力
- 2: 標準エラー出力

ファイルディスクリプタとは、これら入出力をOSが判別する為に割り当てられた番号の事です。

### 2. リダイレクト

よく`>`という記号を見ると思いますが、これがリダイレクトです。

例えば、標準出力に出すだけのシェルスクリプトを作ってみます。

```echo.sh
#/bin/bash

echo "これは標準出力です"
```

普通に実行すれば標準出力に表示されますが、リダイレクトさせると、別の場所に出力する事が出来るようになります。

ファイルにリダイレクトする場合。

```bash
$ ./echo.sh > result.txt
$ cat result.txt
これは標準出力です
```

### 3. 特定のファイルディスクリプタにリダイレクトさせる

標準エラー出力にリダイレクトさせる場合はこのようにします。

```bash
$ ./echo.sh >&2
これは標準出力です
```

逆に標準出力にリダイレクトさせる場合はこうです。

```bash
$ ./echo.sh >&1
これは標準出力です
```

今は意味が分からなくてもOKです。
あとで出てます。

### 4. 出力結果を捨てる

`/dev/null`というのはunixのスペシャルファイルの事で、空ファイルの事です。

```bash
$ ./echo.sh > /dev/null
$
```

これまで標準されてた`これは標準出力です`が表示されなかった事が分かります。
これは、標準出力を`/dev/null`に捨てた為です。

### 5. 結果をマージする

先程から出てくる`>&{ファイルディスクリプタ}`という表現は何かというと、
`{ファイルディスクリプタ}`に結果をマージするという意味です。

**標準出力を標準エラー出力にマージする場合**

`1>&2`

**逆に標準エラー出力を標準出力にマージする場合**

`2>&1`

このように使ってきます。

この時点で、もうタイトルの意味が分かってきますね。

## タイトルにあったコマンドの意味を考えてみる

`command > /dev/null 2>&1`

分けて考えましょう。

- `2>&1`
	- 標準エラー出力の結果を標準出力にマージする
- `> /dev/null`
	- 標準出力を捨てる

つまり、このコマンドの意味は、

**標準エラー出力の結果を標準出力にマージして、/dev/nullに捨てる**

という事だったわけです。
(普通標準エラー出力は捨てない方が良いです(cronで動かす場合))

## クイズ1. 標準出力を標準エラー出力として表示して下さい

ここまでくれば簡単ですね。
段階的に考えればOKです。

標準出力を標準エラー出力として表示するということは、
**表示出力の結果を標準エラー出力の結果にマージ**すればOKです。

```echo.sh
#/bin/bash

echo "これは標準出力を標準エラー出力として表示します" 1>&2
```

こんな感じですね。

```bash
$ ./echo.sh
これは標準出力を標準エラー出力として表示します
```

このままだと、標準出力か標準エラー出力かわかりませんね。
じゃあクイズ2で確かめて見ましょう。

## クイズ2. クイズ1の結果を標準エラー出力と分かるように確認してください

さて、ではここではリダイレクトを使ってみましょう。
先程作ったコードでは、標準エラー出力が表示されているはずなので、
標準エラー出力をファイルにリダイレクトしみます。

まずは、これが標準出力ではないことを確認してみましょう。

```bash
$ ./echo.sh > result.txt
これは標準出力を標準エラー出力として表示します
$ cat result.txt
$ 
```

この場合、下記のようなことが起きてます。

- 標準出力を`result.txt`にリダイレクト
- 標準エラー出力が表示

じゃあ次に、表示エラー出力であることを確認してみます。

```bash
$ ./echo.sh 2>result.txt
$ cat result.txt
これは標準出力を標準エラー出力として表示します
```

今度はコンソールには何も表示されず、ファイルに残りました。
これが標準エラー出力がresult.txtにリダイレクトされた結果です。

## まとめ

- ファイルディスクリプタが何かを理解出来た
- `&>{ファイルディスクリプタ}`や、`{ファイルディスクリプタ}&>{ファイルディスクリプタ}`の意味も理解出来た
- 最初に見ていた`command > /dev/null 2>&1`がどういう意味かも分かった

## 補足

ただし、世間的に2つの話がアリます。

### 1. そもそもこの記述が分かりづらい

- まぁ、普通に見て、`2>&1`とか、`1>&2`とか、初見ではイミフ
- 何で`/dev/null`はリダイレクトさせてるのに、`2>&1`は何も書かないの？
- そもそもどっち先に書くんだっけ

とか色々あります。

これに対して色んな解決索が提示さてますが、1つは標準出力と標準エラー出力を別ファイルにリダイレクトさせるという方法

`command 1>normal.txt 2>error.txt`

分かりやすいですね。
実際にやってみましょう。
`echo.sh`を少し改造します。

```echo.sh
#/bin/bash

echo "これは標準出力です"
echo "これは標準出力を標準エラー出力として表示します" 1>&2
```

実験

```bash
$ ./echo.sh 1>normal.txt 2>error.txt
$ cat normal.txt
これは標準出力です
$ cat error.txt
これは標準出力を標準エラー出力として表示します
```

想定通りですね。そして`command > /dev/null 2>&1`よりは分かりやすいです。

もう1つは個人的な意見ですが、cronで動かすようなバッチとかは、
標準出力には出すような内容は、ログファイルに出すようにしておくのが良いかと思ってます。


### 2. 標準エラー出力を捨てるな

最初の`command > /dev/null 2>&1`というのは、これまで説明してきた通り、
**標準エラー出力を標準出力の結果にマージして、/dev/nullに捨てる**という事をしてます。

つまり、`command`(何かしらのスクリプト)で標準エラー出力すらも捨ててるので、
何か問題が起きた時に気づけないという問題があります。

cronで動かす場合は、`crontab`にMAILTOを書くのが定石ですが、全て`> /dev/null 2>&1`してしまっていると、このMAILTOには何も送られてこないので意味なくなります。

せめて標準エラー出力が起きたらメールで気付ける。
くらいの運用は最低限出来ておいた方が良いかと思います。

## 今後

これで、あなたも明日からちゃんと理解しながらcrontabを書ける事になるでしょう。
今のcrontabを見直して、おかしな表現だったり、分かりづらい表現であればチームに提言してみてはいかがでしょうか。