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 tee
  • set -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期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?