- サーバー
Linuxリダイレクト 2>&1の意味と順序|標準エラー出力・tee
2>&1 とは、「標準エラー出力(2番)の行き先を、いまの標準出力(1番)と同じ場所にする」という指定です。 リダイレクトは左から順に処理されるので、> file 2>&1 と書けば両方がファイルに入り、2>&1 > file と書くと標準エラー出力は画面に残ります。
この記事では、>・>>・2>・2>&1・&>・|&・<<・tee などの記法を一覧表にまとめ、順序による違いを実行結果と図で確認します。さらに、sh(dash)で &> が意図どおり動かない例、sudo tee、noclobber、exec でスクリプト全体をログに残す書き方まで、bash 5.2.21 と dash 0.5.12 で実行した出力つきで紹介します。
リダイレクト記法の早見表
記法と出力先の一覧
| 記法 | 意味 | 標準出力(1) | 標準エラー(2) | sh(dash) |
|---|---|---|---|---|
cmd > file |
標準出力をファイルへ(上書き) | file | 画面 | 使える |
cmd >> file |
標準出力をファイルへ(追記) | file の末尾 | 画面 | 使える |
cmd 2> file |
標準エラーをファイルへ(上書き) | 画面 | file | 使える |
cmd 2>> file |
標準エラーをファイルへ(追記) | 画面 | file の末尾 | 使える |
cmd > file 2>&1 |
両方を同じファイルへ | file | file | 使える |
cmd >> file 2>&1 |
両方を同じファイルへ追記 | file の末尾 | file の末尾 | 使える |
cmd 2>&1 > file |
順序が逆の形 | file | 画面 | 使える |
cmd &> file |
両方を同じファイルへ(短縮形) | file | file | bash 専用 |
cmd &>> file |
両方を同じファイルへ追記(短縮形) | file の末尾 | file の末尾 | bash 専用 |
cmd > out 2> err |
別々のファイルへ | out | err | 使える |
cmd > /dev/null 2>&1 |
両方を捨てる | 捨てる | 捨てる | 使える |
cmd 2> /dev/null |
エラーだけ捨てる | 画面 | 捨てる | 使える |
cmd1 | cmd2 |
標準出力を次のコマンドへ | cmd2 の入力 | 画面 | 使える |
cmd1 2>&1 | cmd2 |
両方を次のコマンドへ | cmd2 の入力 | cmd2 の入力 | 使える |
cmd1 |& cmd2 |
同上(短縮形) | cmd2 の入力 | cmd2 の入力 | bash 専用 |
echo msg >&2 |
メッセージを標準エラーへ | なし | 画面 | 使える |
cmd >| file |
noclobber でも上書きする | file | 画面 | 使える |
入力と tee の一覧
| 記法 | 意味 | sh(dash) |
|---|---|---|
cmd < file |
ファイルの内容を標準入力へ | 使える |
cmd <<EOS 〜 EOS |
ヒアドキュメント。複数行をそのまま標準入力へ(変数は展開される) | 使える |
cmd <<'EOS' 〜 EOS |
変数を展開しないヒアドキュメント | 使える |
cmd <<-EOS 〜 EOS |
行頭のタブを取り除くヒアドキュメント | 使える |
cmd <<< "文字列" |
ヒアストリング。1つの文字列を標準入力へ | bash 専用 |
cmd | tee file |
画面に出しながらファイルにも保存(上書き) | 使える |
cmd | tee -a file |
画面に出しながらファイルに追記 | 使える |
cmd 2>&1 | tee file |
標準エラーも含めて画面とファイルへ | 使える |
echo 内容 | sudo tee file |
管理者権限が必要なファイルへ書く | 使える |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Linuxリダイレクト 2>&1の意味と順序|標準エラー出力・tee」
標準入力・標準出力・標準エラー出力の基本
Linux のコマンドは、起動した時点で3つの入出力の口を持っています。番号はファイルディスクリプタ(fd)と呼ばれます。
| 番号 | 名前 | 既定の行き先 | 使いみち |
|---|---|---|---|
| 0 | 標準入力(stdin) | キーボード | コマンドへの入力 |
| 1 | 標準出力(stdout) | 画面 | 本来の結果 |
| 2 | 標準エラー出力(stderr) | 画面 | エラーや警告、進み具合のメッセージ |
どちらも画面に出るので見た目では区別できませんが、別々の口です。確認用に、両方へ1行ずつ出すスクリプトを用意します。以降の例はすべてこの both.sh を使います。
#!/bin/bash
echo "OUT: 標準出力の行"
echo "ERR: 標準エラーの行" >&2
検証環境は bash 5.2.21、dash 0.5.12(/bin/sh の実体)、GNU coreutils 9.4 です。
$ ./both.sh
OUT: 標準出力の行
ERR: 標準エラーの行
$ ./both.sh > out.txt
ERR: 標準エラーの行
$ cat out.txt
OUT: 標準出力の行
$ ./both.sh 2> err.txt
OUT: 標準出力の行
$ cat err.txt
ERR: 標準エラーの行
$ ./both.sh >> out.txt; cat out.txt
ERR: 標準エラーの行
OUT: 標準出力の行
OUT: 標準出力の行
> は「1>」の省略形で、標準出力だけをファイルへ向けます。そのため > out.txt としても ERR: の行は画面に残ります。2> と書くと標準エラー出力だけがファイルに入ります。>> は追記なので、out.txt は2行になりました。
2>&1 の意味と、順序で結果が変わる理由
2>&1 は「2番を、1番がいま向いている先と同じ場所へ向ける」という意味です。&1 の & は「1という名前のファイルではなく、1番の口」という目印です。
実行結果で比べる
$ ./both.sh > all.txt 2>&1
$ cat all.txt
OUT: 標準出力の行
ERR: 標準エラーの行
$ ./both.sh 2>&1 > only.txt
ERR: 標準エラーの行
$ cat only.txt
OUT: 標準出力の行
| 書き方 | 画面 | ファイル |
|---|---|---|
> all.txt 2>&1 |
何も出ない | 両方の行が入る |
2>&1 > only.txt |
ERR: の行が出る |
標準出力の行だけ |
図で理解する(左から順に処理される)
シェルはリダイレクトを左から1つずつ処理します。2>&1 は「その時点の1番の行き先」をコピーするだけで、あとから1番を変えても2番はついていきません。
> file 2>&1 の場合:
flowchart LR
subgraph s0["はじめ"]
a1["1 標準出力"] --> ta["画面"]
a2["2 標準エラー"] --> ta
end
subgraph s1["手順1 1番を file へ"]
b1["1 標準出力"] --> fb["file"]
b2["2 標準エラー"] --> tb["画面"]
end
subgraph s2["手順2 2番を1番と同じ先へ"]
c1["1 標準出力"] --> fc["file"]
c2["2 標準エラー"] --> fc
end
s0 --> s1 --> s2
2>&1 > file の場合:
flowchart LR
subgraph s0["はじめ"]
a1["1 標準出力"] --> ta["画面"]
a2["2 標準エラー"] --> ta
end
subgraph s1["手順1 2番を1番と同じ先へ(=画面)"]
b1["1 標準出力"] --> tb["画面"]
b2["2 標準エラー"] --> tb
end
subgraph s2["手順2 1番を file へ"]
c1["1 標準出力"] --> fc["file"]
c2["2 標準エラー"] --> tc["画面"]
end
s0 --> s1 --> s2
後者では、手順1の時点で1番はまだ画面を向いているので、2番も画面を向きます。そのあと1番だけがファイルへ切り替わります。
行き先を直接のぞいて確かめる
Linux では /proc/self/fd/番号 を見ると、その口がどこにつながっているかがわかります。端末上で実行した結果です(パスはファイル名だけに短縮しています)。
$ readlink /proc/self/fd/1 /proc/self/fd/2 > fd_a.txt 2>&1
$ cat fd_a.txt
fd_a.txt
fd_a.txt
$ readlink /proc/self/fd/1 /proc/self/fd/2 2>&1 > fd_b.txt
$ cat fd_b.txt
fd_b.txt
/dev/pts/0
> fd_a.txt 2>&1 では1番も2番も fd_a.txt、2>&1 > fd_b.txt では2番は /dev/pts/0(端末=画面)のままです。図のとおりの結果になりました。
書き間違えやすい形
| 書いたもの | 起きること | 正しい形 |
|---|---|---|
cmd 2>1 |
1 という名前のファイルができ、そこにエラーが入る |
cmd 2>&1 |
cmd 2>&1 > file |
エラーは画面に出る | cmd > file 2>&1 |
cmd > file 2> file |
2つの口が別々に同じファイルを開き、互いの内容を上書きし合う | cmd > file 2>&1 |
$ ./both.sh 2>1; ls 1; cat 1
OUT: 標準出力の行
1
ERR: 標準エラーの行
& を忘れると、カレントディレクトリに 1 というファイルが作られます。見覚えのない 1 があったら、この書き間違いを疑ってみてください。
同じファイルを > と 2> で別々に指定した場合も試しました。標準出力に A1〜A3、標準エラーに B1〜B3 を交互に出すループです。
$ for i in 1 2 3; do echo "A$i"; echo "B$i" >&2; done > same.txt 2> same.txt; cat same.txt
B1
B2
B3
$ for i in 1 2 3; do echo "A$i"; echo "B$i" >&2; done > same2.txt 2>&1; cat same2.txt
A1
B1
A2
B2
A3
B3
> same.txt 2> same.txt では A の行がすべて消えました。2つの口がそれぞれファイルの先頭から書くため、同じ位置に上書きされたからです。2>&1 なら1つの口を共有するので、出た順に6行そろいます。
&>・&>>・|& でまとめて書く(bash)
bash には、両方をまとめる短縮形があります。Bash の公式マニュアルでは、&>file は >file 2>&1 と、&>>file は >>file 2>&1 と同じ意味だと説明されています。
$ ./both.sh &> amp.txt; cat amp.txt
OUT: 標準出力の行
ERR: 標準エラーの行
$ ./both.sh &>> amp.txt; wc -l < amp.txt
4
$ ./both.sh | grep -c 行
ERR: 標準エラーの行
1
$ ./both.sh 2>&1 | grep -c 行
2
$ ./both.sh |& grep -c 行
2
パイプ | が渡すのは標準出力だけなので、./both.sh | grep -c 行 では ERR: の行が画面に素通りし、数えられたのは1行です。2>&1 | または |& と書くと、標準エラー出力もパイプに流れます。
| 短縮形(bash) | 同じ意味の書き方(sh でも動く) |
|---|---|
cmd &> file |
cmd > file 2>&1 |
cmd &>> file |
cmd >> file 2>&1 |
cmd1 |& cmd2 |
cmd1 2>&1 | cmd2 |
標準エラー出力だけをパイプに流す
順序の仕組みを使うと、エラーだけを次のコマンドに渡すこともできます。
$ ./both.sh 2>&1 > /dev/null | sed "s/^/[エラーのみ] /"
[エラーのみ] ERR: 標準エラーの行
パイプへの接続が最初に行われ、2>&1 で2番がパイプへ、続く > /dev/null で1番だけが捨てられます。エラーメッセージだけを grep で絞り込みたいときに使える形です。
パイプの場合は 2>&1 を | の前に書きます。パイプへの接続が先に行われ、そのあとで 2>&1 が処理されるため、この位置で両方がパイプに入ります。
標準出力と標準エラー出力を別ファイルに分ける・/dev/null に捨てる
別々のファイルへ
$ ./both.sh > o.log 2> e.log; head o.log e.log
==> o.log <==
OUT: 標準出力の行
==> e.log <==
ERR: 標準エラーの行
結果は o.log、エラーは e.log に分かれました。エラーだけをあとで確認したい定期処理で役立つ形です。
/dev/null へ捨てる
/dev/null は、書き込まれた内容をすべて捨てる特別なファイルです。
$ ./both.sh > /dev/null
ERR: 標準エラーの行
$ ./both.sh 2> /dev/null
OUT: 標準出力の行
$ ./both.sh > /dev/null 2>&1; echo "終了コード=$?"
終了コード=0
$ ./both.sh 2>&1 > /dev/null
ERR: 標準エラーの行
| 目的 | 書き方 |
|---|---|
| 結果は不要、エラーだけ見たい | cmd > /dev/null |
| エラーメッセージだけ消したい | cmd 2> /dev/null |
| 何も表示せず、成否だけ知りたい | cmd > /dev/null 2>&1($? や if で判定) |
| 順序が逆だと | cmd 2>&1 > /dev/null はエラーが画面に残る |
出力を捨てても終了コードは変わらないので、if command -v git > /dev/null 2>&1; then ... のように「あるかどうかだけ知りたい」場面でよく使います。エラーを捨てると原因の手がかりも消えるので、調査中はファイルに残す形がおすすめです。
sh(dash)では &> が意図どおりに動かない
&>・&>>・|&・<<< は bash の拡張です。Ubuntu や Debian では /bin/sh の実体が dash で、#!/bin/sh のスクリプトや sh -c、cron の既定のシェルでは dash が使われます。dash 0.5.12 で実行した結果です。
$ sh -c './both.sh &> d2.txt; echo 次のコマンドへ進んだ; sleep 0.3; wc -c d2.txt'
次のコマンドへ進んだ
OUT: 標準出力の行
ERR: 標準エラーの行
0 d2.txt
$ sh -c './both.sh |& cat'
sh: 1: Syntax error: "&" unexpected
$ sh -c 'cat <<< hello'
sh: 1: Syntax error: redirection unexpected
$ sh -c './both.sh > d1.txt 2>&1; cat d1.txt'
OUT: 標準出力の行
ERR: 標準エラーの行
|& と <<< は構文エラーで気づけます。一方 &> はエラーにならず、別の意味で実行されます。dash は ./both.sh &> d2.txt を次の2つに分けて解釈します。
| dash の解釈 | 内容 |
|---|---|
./both.sh & |
both.sh をバックグラウンドで実行(出力は画面へ) |
> d2.txt |
コマンドなしのリダイレクト。空のファイルを作るだけ |
その結果、出力は画面に出て、d2.txt は 0 バイト、しかもコマンドの終了を待たずに次へ進みます。ログが空のまま、という現象の原因になりやすい箇所です。
| 状況 | おすすめの書き方 |
|---|---|
#!/bin/sh のスクリプト、sh -c、crontab |
> file 2>&1/>> file 2>&1/2>&1 | |
#!/bin/bash のスクリプト、bash の対話シェル |
&>/&>>/|& も使える |
| どちらで動くかわからない | > file 2>&1 に統一する |
入力のリダイレクト(<・<<・<<<)
出力と同じように、標準入力もファイルや文字列に切り替えられます。
$ printf "b\na\nc\n" > in.txt
$ sort < in.txt
a
b
c
$ sort < in.txt > sorted.txt; cat sorted.txt
a
b
c
$ wc -l <<< "1行だけ渡す"
1
ヒアドキュメント(<<)は、終わりの目印(ここでは EOS)までの複数行をそのまま標準入力にします。目印をクォートすると変数が展開されません。
$ name=RJC; cat <<EOS
こんにちは $name
EOS
こんにちは RJC
$ cat <<'EOS'
こんにちは $name
EOS
こんにちは $name
$ cat > conf.txt <<EOS
host=localhost
port=7500
EOS
$ cat conf.txt
host=localhost
port=7500
| 記法 | 変数・コマンド置換 | 用途 |
|---|---|---|
<<EOS |
展開される | 変数を埋め込んだ設定ファイルやメッセージを作る |
<<'EOS' |
展開されない | $ を含むスクリプトや設定をそのまま書き出す |
<<-EOS |
展開される | 行頭のタブを取り除く(if の中で字下げしたいとき) |
<<< "文字列" |
展開される | 1行だけ渡す(bash 専用) |
while read でファイルを1行ずつ処理する
ループ全体にも入力のリダイレクトを付けられます。done のうしろに < ファイル と書きます。
$ printf "りんご 3\nみかん 5\n" > stock.txt
$ while read -r name qty; do echo "$name は $qty 個"; done < stock.txt
りんご は 3 個
みかん は 5 個
$ total=0; while read -r name qty; do total=$((total + qty)); done < stock.txt; echo "合計=$total"
合計=8
$ total=0; cat stock.txt | while read -r name qty; do total=$((total + qty)); done; echo "合計=$total"
合計=0
cat ファイル | while ... の形では 合計が 0 のままです。パイプの右側は別のプロセス(サブシェル)で動くため、ループの中で変えた変数が外に伝わりません。ループの結果を変数に残したいときは done < ファイル の形にします。
読むファイルと書くファイルを同じにすると空になる
$ sort in.txt > in.txt; wc -c in.txt
0 in.txt
シェルはコマンドを実行する前に > in.txt でファイルを空にします。そのあと sort が読むので、結果は空です。この動きを逆に利用して、: > ファイル と書くとファイルを空にできます(: は何もしないコマンドです。7バイトの keep.txt が 0 バイトになることを確認しました)。内容を残したいときは、sort in.txt -o in.txt のようにコマンド側の機能を使うか、sort in.txt > tmp.txt && mv tmp.txt in.txt と一時ファイルを経由すると、内容を保ったまま置き換えられます。
tee で画面に出しながらファイルにも保存する
tee は、標準入力で受け取った内容を画面(標準出力)とファイルの両方に書き出すコマンドです。
$ ./both.sh | tee t.log
OUT: 標準出力の行
ERR: 標準エラーの行
$ cat t.log
OUT: 標準出力の行
画面には両方見えていますが、t.log に入ったのは標準出力の行だけです。ERR: の行はパイプを通らず、直接画面に出ています。
標準エラー出力も tee で保存する
$ ./both.sh 2>&1 | tee t.log > /dev/null; cat t.log
OUT: 標準出力の行
ERR: 標準エラーの行
$ ./both.sh |& tee -a t.log > /dev/null; wc -l < t.log
4
(画面への表示が重ならないよう、この例では tee の標準出力を /dev/null に向けています。ふだんは > /dev/null を付けずに使います。)
| やりたいこと | 書き方 |
|---|---|
| 画面とファイルへ(上書き) | cmd | tee file |
| 画面とファイルへ(追記) | cmd | tee -a file |
| エラーも含めて保存 | cmd 2>&1 | tee file |
| 複数のファイルへ同時に | cmd | tee a.log b.log |
| 標準エラーだけを保存しつつ画面にも出す(bash) | cmd 2> >(tee err.log >&2) |
最後の形は bash のプロセス置換を使います。標準出力と標準エラーを分けたまま、エラーだけを画面とファイルの両方に出せます。
$ ./both.sh 2> >(tee e2.log >&2) > o2.log; sleep 0.2; head o2.log e2.log
ERR: 標準エラーの行
==> o2.log <==
OUT: 標準出力の行
==> e2.log <==
ERR: 標準エラーの行
tee を挟むと終了コードが変わる
パイプラインの終了コードは、既定では最後のコマンド(ここでは tee)のものになります。
$ false | tee x.log; echo "終了コード=$? PIPESTATUS=${PIPESTATUS[*]}"
終了コード=0 PIPESTATUS=1 0
$ set -o pipefail; false | tee x.log; echo "終了コード=$?"
終了コード=1
false はエラー(終了コード 1)ですが、tee が成功したので全体は 0 になりました。set -o pipefail を有効にするか、bash の ${PIPESTATUS[0]} を見ると、前のコマンドの結果を拾えます。
sudo tee で管理者権限のファイルに書く
一般ユーザーで /etc 以下のファイルに書き込むとき、sudo echo ... > file ではうまくいきません。リダイレクトを行うのは sudo の外側の、自分のシェルだからです。検証用に作った一般ユーザーで実行した結果です(メッセージ先頭のスクリプト名は bash に置き換えています)。
$ whoami
t150u
$ echo "max=10" > /etc/t150demo.conf
bash: /etc/t150demo.conf: Permission denied
$ sudo echo "max=10" > /etc/t150demo.conf
bash: /etc/t150demo.conf: Permission denied
$ echo "max=10" | sudo tee /etc/t150demo.conf
max=10
$ echo "min=1" | sudo tee -a /etc/t150demo.conf > /dev/null
$ sudo sh -c 'echo "step=2" >> /etc/t150demo.conf'
$ cat /etc/t150demo.conf
max=10
min=1
step=2
| 書き方 | 結果 | 理由 |
|---|---|---|
sudo echo x > file |
Permission denied | > は自分の権限で開かれる |
echo x | sudo tee file |
書ける | ファイルを開くのは管理者権限の tee |
echo x | sudo tee -a file > /dev/null |
追記でき、画面には出さない | -a で追記、表示は捨てる |
sudo sh -c 'echo x >> file' |
書ける | リダイレクトごと管理者権限のシェルで実行 |
tee は -a を付けないと上書きです。設定ファイルに1行足すつもりのときは -a を忘れずに付けましょう。
noclobber で上書きを防ぐ(set -C と >|)
> は既存のファイルを確認なしで空にします。set -C(set -o noclobber)を有効にすると、既存ファイルへの > をエラーにして守れます。
$ echo first > keep.txt
$ set -C
$ echo second > keep.txt; echo "終了コード=$?"
bash: line 1: keep.txt: cannot overwrite existing file
終了コード=1
$ echo third >> keep.txt; cat keep.txt
first
third
$ echo forced >| keep.txt; cat keep.txt
forced
$ echo x &> keep.txt
bash: line 1: keep.txt: cannot overwrite existing file
$ set +C
| 操作 | noclobber 有効時 |
|---|---|
> 既存ファイル |
cannot overwrite existing file で止まる(終了コード 1) |
> 新しいファイル |
作成できる |
>> 既存ファイル |
追記できる |
>| 既存ファイル |
意図して上書きできる |
&> 既存ファイル |
> と同じく止まる |
| 解除 | set +C |
dash でも set -C と >| は使えます(sh: 1: cannot create d1.txt: File exists と表示されました)。対話シェルで常に有効にしたいときは ~/.bashrc に set -o noclobber を書きます。
exec でスクリプト全体の出力をログに残す
スクリプトの各行に >> log 2>&1 を付ける代わりに、先頭で exec を使うと、それ以降のすべての出力の行き先をまとめて変えられます。
#!/bin/bash
exec >> job.log 2>&1 # ここから先の出力はすべて job.log へ追記
echo "開始"
ls /no/such/dir
echo "終了 (ls の終了コード=$?)"
$ ./job.sh; ./job.sh; cat job.log
開始
ls: cannot access '/no/such/dir': No such file or directory
終了 (ls の終了コード=2)
開始
ls: cannot access '/no/such/dir': No such file or directory
終了 (ls の終了コード=2)
2回実行しても画面には何も出ず、echo の出力も ls のエラーも job.log に追記されています。
画面にも出しながらログに残す
#!/bin/bash
exec > >(tee -a job2.log) 2>&1 # 画面にも出しつつ job2.log にも追記
echo "開始"
ls /no/such/dir
echo "終了"
$ ./job2.sh; sleep 0.3; echo "--- job2.log ---"; cat job2.log
開始
ls: cannot access '/no/such/dir': No such file or directory
終了
--- job2.log ---
開始
ls: cannot access '/no/such/dir': No such file or directory
終了
プロセス置換 >( ... ) を使うので、この形は #!/bin/bash のスクリプトで使います。
一時的に切り替えて元に戻す
#!/bin/bash
exec 3>&1 # いまの標準出力を 3 番に控える
exec > part.log # 標準出力をファイルへ
echo "この行はファイルへ"
exec 1>&3 3>&- # 標準出力を元に戻し、3 番を閉じる
echo "この行は画面へ"
$ ./job3.sh; cat part.log
この行は画面へ
この行はファイルへ
3番以降の番号は自由に使える口です。行き先を控えておき、あとで戻すことができます。一部の処理だけなら、{ cmd1; cmd2; } > part.log 2>&1 と波かっこでまとめる書き方も手軽です。
自作スクリプトのエラーメッセージは >&2 で出す
自分で書くスクリプトでも、エラーや警告は標準エラー出力へ出すと、使う側が結果とメッセージを分けて扱えます。
$ die() { echo "エラー: $*" >&2; return 1; }; die "設定ファイルがありません" > /dev/null; echo "終了コード=$?"
エラー: 設定ファイルがありません
終了コード=1
標準出力を /dev/null に捨てても、>&2 で出したメッセージは画面に残っています。>&2 は 1>&2 の省略形で、「1番を、いまの2番と同じ行き先へ」という意味です。
| やりたいこと | 書き方 |
|---|---|
| スクリプト全体をログへ追記 | exec >> file 2>&1 |
| 全体を画面とログの両方へ(bash) | exec > >(tee -a file) 2>&1 |
| 数行だけまとめてリダイレクト | { cmd1; cmd2; } > file 2>&1 |
| スクリプトからエラーメッセージを出す | echo "エラー: ..." >&2 |
cron でログを残す定番の書き方
cron で動かすコマンドは画面がないので、出力をファイルへ残す指定が欠かせません。定番は次の形です(cron 自体はこの記事では動かしていません。リダイレクト部分は上の dash での実行結果のとおりです)。
# 毎日 3:00 に実行し、結果もエラーもログへ追記
0 3 * * * /home/app/bin/backup.sh >> /home/app/log/backup.log 2>&1
# 出力をすべて捨てる
*/5 * * * * /home/app/bin/check.sh > /dev/null 2>&1
| ポイント | 理由 |
|---|---|
>> で追記する |
> だと実行のたびに前回のログが消える |
2>&1 はファイル指定のあとに書く |
逆順だとエラーがログに入らない |
&>> ではなく >> file 2>&1 |
cron の既定のシェルは /bin/sh。dash では &> が別の意味になる |
| コマンドやログはフルパスで | cron の環境は PATH やカレントディレクトリが対話シェルと異なる |
crontab の時刻の書き方や環境変数の扱いは、別記事「cronの書き方|設定例30パターンの早見表と落とし穴まとめ」で詳しく扱っています。
目的別の書き方(逆引き)
| やりたいこと | 書き方 |
|---|---|
| コマンドがあるかどうかだけ調べる | command -v git > /dev/null 2>&1(あれば終了コード 0、なければ 1) |
| 権限エラーの表示を消して検索する | find / -name "*.conf" 2> /dev/null |
| ビルドの出力を見ながらファイルにも残す | make 2>&1 | tee build.log |
| エラーも含めてページ送りで読む | cmd 2>&1 | less |
| エラーの行だけを絞り込む | cmd 2>&1 > /dev/null | grep -i "no such" |
| ログアウト後も動かし、出力はログへ | nohup cmd > out.log 2>&1 & |
| ファイルを空にする | : > file |
| 複数行の設定ファイルを作る | cat > file <<'EOS' 〜 EOS |
| 管理者権限のファイルに1行足す | echo "行" | sudo tee -a file > /dev/null |
| スクリプト全体をログへ | 先頭に exec >> file 2>&1 |
書いたあとのチェックリスト
| 確認すること | 見るところ |
|---|---|
2>&1 はファイル指定のあとにあるか |
> file 2>&1 の順 |
& を付け忘れていないか |
2>1 になっていないか、1 というファイルができていないか |
| 実行するシェルは bash か sh か | 先頭行(#!/bin/sh か #!/bin/bash か)、crontab かどうか |
| 上書きと追記を取り違えていないか | > と >>、tee と tee -a |
tee を挟んだあとの終了コード |
set -o pipefail または ${PIPESTATUS[0]} |
| 読むファイルと書くファイルが同じでないか | cmd file > file の形 |
よくある質問
Q. > file 2>&1 と &> file はどちらを使えばよいですか?
A. bash で動かすと決まっているなら、どちらも同じ結果です。#!/bin/sh のスクリプトや crontab など、dash で実行される可能性がある場所では > file 2>&1 を使います。迷ったら > file 2>&1 に統一しておくと、どのシェルでも同じ動きになります。
Q. 2>&1 の & を付け忘れるとどうなりますか?
A. 2>1 は「標準エラー出力を 1 という名前のファイルへ」という意味になり、カレントディレクトリに 1 というファイルができます。エラーにはならないので、見覚えのない 1 があったらこの書き間違いを確認してみてください。
Q. > file 2> file と > file 2>&1 は同じですか?
A. 同じにはなりません。前者は2つの口が別々にファイルを開くため、互いの内容を上書きし合います。本文の実行例では、標準出力の3行がすべて消えました。同じファイルに入れるときは > file 2>&1(追記なら >> file 2>&1)と書きます。
Q. ログを日付ごとのファイルに分けるにはどう書きますか?
A. ファイル名にコマンド置換を使い、cmd >> "app_$(date +%F).log" 2>&1 と書くと、app_2026-10-02.log のように日付入りのファイルへ追記されます(手元で確認)。crontab の中では % が特別な意味を持つので、date +\%F のようにバックスラッシュを付けるか、日付の処理をスクリプトの中に入れる形が扱いやすいです。
Q. command > /dev/null 2>&1 & の最後の & は何ですか?
A. リダイレクトとは別の記号で、コマンドをバックグラウンドで実行する指定です。2>&1 や &> の & とは役割が違います。dash で &> が「バックグラウンド実行+空ファイル作成」になるのは、この & として解釈されるためです。
まとめ
2>&1は「2番を、いまの1番と同じ行き先にする」。リダイレクトは左から順に処理される- 両方をファイルへ入れるなら
> file 2>&1。2>&1 > fileだと標準エラー出力は画面に残る &>・&>>・|&・<<<は bash 専用。dash では&>がバックグラウンド実行と空ファイル作成になる- 別々に残すなら
> out.log 2> err.log、捨てるなら> /dev/null 2>&1 teeは画面とファイルの両方へ。エラーも残すなら2>&1 | tee、追記は-a、管理者権限のファイルはsudo teeset -Cで上書きを防ぎ、>|で意図した上書き。スクリプト全体のログは先頭のexec >> file 2>&1- cron では
>> ログ 2>&1の順で書く
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Linuxリダイレクト 2>&1の意味と順序|標準エラー出力・tee」
株式会社RJCでは、Linuxのリダイレクトや標準エラー出力の扱いを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。