UNIX/Linuxの部屋 用語集:リダイレクト

TOP UNIX/Linuxの部屋 UNIX/Linuxコマンド一覧 用語集 新版 由来/読み方辞書 環境変数マニュアル Cシェル変数 システム設定ファイル システムコール・ライブラリ ネットワークプログラミングの基礎知識 クラウドサービス徹底比較・徹底解説




用語集 リダイレクト コマンドの出力をファイルや別のコマンドに振り分ける (リダイレクション・パイプ) このエントリーをはてなブックマークに追加

最終更新


UNIX/Linux のシェル sh・bash・csh・tcsh のリダイレクトを説明する。ファイルへの出力、コマンド出力を別のコマンドの入力とする、標準入力・標準出力・標準エラー出力、パイプなどもあわせて説明する。



リダイレクトの基礎
コマンドを実行したとき、その出力をファイルに書き込みたい場合は
% command > file.txt
とする。

コマンドを実行したとき、その出力を別のコマンドの入力として渡したい場合は
% command | command2
とする。これを「パイプで繋ぐ」と言う。
% command | command2 | command3 | command4
と、パイプで多段に接続することもできる。

標準出力と標準エラー出力
UNIX では、2つの出力先がある。標準出力と標準エラー出力である。

ls コマンドの出力をファイルに書き込む場合を考えてみよう。拡張子が .txt のファイル名の一覧をファイル output に書き込むには
% ls *.txt > output
とすればよい。しかし拡張子が .txt であるファイルがひとつも存在しなかったらどうなるだろうか。出力先を output にリダイレクトしているにも関わらず、ls コマンドは
% ls *.txt > output
ls: No match.
というエラーを出力する。なぜなら、エラーが出たということはユーザが気づくべきことなので、リダイレクトの指定があったからといって、ファイルに書き込むのは望ましくないからである。

これを実現しているのが、標準出力と標準エラー出力である。ls コマンドはファイル一覧などの通常の出力は標準出力に書き出す。しかし、No match やオプションの誤りなどでエラーが起こった場合は、標準エラー出力に書き出すのである。他のコマンドも ls コマンドと同様に、
  • 普通の出力は標準出力に
  • ユーザに気づいてほしいエラー表示などは、標準エラー出力に
という方針でプログラムが作成されている。

しかし、標準エラー出力もファイルにリダイレクトしたり、パイプで他のコマンドに渡したい場合がある。ここからは sh・bash と csh・tcsh で書式が大きく違うので、分けて記述する。

sh・bash のリダイレクト一覧
sh・bash で使用できるリダイレクトの一覧を下記に示す。たくさんありすぎるので初心者は「いろんなのリダイレクトがある」と思っておくだけでよい。
>[ファイル] ファイルに出力する
n>[ファイル] ファイルに出力するディスクリプタn を新規作成する。
>>[ファイル] ファイルに追記する
n>>[ファイル] ファイルに追記するディスクリプタn を新規作成する。
<[ファイル] ファイル内容を標準入力から読み込む
n<[ファイル] ファイル内容を読むディスクリプタn を新規作成する。
&>[ファイル] 標準出力と標準エラー出力をまとめてファイルにリダイレクト。bash 拡張
>&[ファイル] 標準出力と標準エラー出力をまとめてファイルにリダイレクト。bash 拡張
&>>[ファイル] 標準出力と標準エラー出力をまとめてファイルに追記。bash 拡張
>(コマンド) 別のコマンドに対して標準出力を渡す。プロセス置換と呼ばれる。bash 拡張
<(コマンド) 別のコマンドの結果を標準入力から受け取る。プロセス置換と呼ばれる。bash 拡張
n<&m 入力ディスクリプタm を複製してディスクリプタ n を生成する。
<&m 入力ディスクリプタm を複製してディスクリプタ 0 を生成する。
n>&m 出力ディスクリプタm を複製してディスクリプタ n を生成する。
>&m 出力ディスクリプタm を複製してディスクリプタ 1 を生成する。
>n<&m- 入力ディスクリプタm を複製してディスクリプタ n を生成し、m をクローズする
>n>&m- 出力ディスクリプタm を複製してディスクリプタ n を生成し、m をクローズする

あまり使わないであろうものは下記にまとめた。
<>[ファイル] 読み書き両用でオープン
>|[ファイル] ファイルに出力する。ファイル上書き不可モードでも出力する。

sh・bash の場合
まず、sh・bash においては、下記のルールを覚えてほしい (ちなみに標準入力は 0 番)。
  • 標準出力は 1 番
  • 標準エラー出力は 2 番

標準出力を file1 に、標準エラー出力を file2 に、などと振り分けるには下記のように書く。
% command 1>file1 2>file2
いずれかを /dev/null に捨てることで、どちらかだけを表示することも簡単に指定できる。
% command 1>/dev/null
→ 標準出力を捨て、標準エラー出力のみを画面に出力する。
% command 2>/dev/null
→ 標準エラー出力を捨て、標準出力のみを画面に出力する。

上記の find のように標準エラー出力を除外したい場合は、下記のようにすればよい。
% find / 2>/dev/null

標準出力と標準エラー出力を両方まとめて他のコマンドに渡すには:
% command 2>&1 | less

標準出力と標準エラー出力をまとめて file に書き出す場合は:
% command >file 2>&1

ここで順番を逆にしてしまうと、うまくいかないことに注意 (理由は後述)。
% command 2>&1 >file (誤り!)

標準エラー出力のみをページャなどで見たい場合:
% command 2>&1 1>/dev/null | less
これも順番を逆にするとうまくいかない。
% command 1>/dev/null 2>&1 | less (誤り!)

より複雑な例を紹介しよう。

標準エラー出力のみをパイプに出力する。
% command 2>&1 >/dev/null | command2
標準エラー出力のみをパイプに (出力を閉じるので、command が出力結果をチェックしているならエラーになる)。
% command 2>&1 >&- | command2
標準出力と標準エラー出力を交換する。
% command 3>&1 1>&2 2>&3
標準出力を捨て、標準エラー出力をページャで参照する。
% command 3>&1 >/dev/null 2>&3 | less

sh・bash におけるリダイレクトの順序
さて、先にも述べたように、標準出力と標準エラー出力をファイルに出力する場合は下記のようにする。
% command 1>file 2>&1
% command >file 2>&1 (1 を省略してもよい)
しかし順番を逆にするのは誤りである。
% command 2>&1 1>file
→ 誤り!
% command 2>&1 >file
→ 誤り!

なぜ上の書き方が正しいのか、なぜ下の書き方ではダメなのか説明できるだろうか。なお、「リダイレクトは右に書いたものから順に評価されるから」は間違いである。

そもそも「2>&1」という書き方は「2 の出力先を 1 にマージする」というイメージで捉えている人が多いのではないだろうか。「2>&1」の本当の意味は「2 の出力先を、1 の出力先と同じものに設定する」である。

まず、リダイレクトを指定しない場合は、下記のようになっている。
% command
⇒ 1 の出力先 … 画面
⇒ 2 の出力先 … 画面

標準出力のみをファイルにリダイレクトした場合、下記のようになる。
% command 1>file
⇒ 1 の出力先 … file
⇒ 2 の出力先 … 画面

では標準出力と標準エラー出力をファイル file に出力する
% command 1>file 2>&1
についてだが、これはまず「1>file」が処理されて、
⇒ 1 の出力先 … file
⇒ 2 の出力先 … 画面
となり、その後に「2>&1」が処理されて、
⇒ 1 の出力先 … file
⇒ 2 の出力先 … file (なぜなら「2 の出力先を、1 の出力先と同じものに設定した」から)
となって、めでたく標準出力と標準エラー出力が file に出力される。

一方、誤った書き方の
% command 2>&1 1>file
であるが、リダイレクト解析前の初期状態は
⇒ 1 の出力先 … 画面
⇒ 2 の出力先 … 画面
となっている。ここでシェルが「2>&1」を処理しても、
⇒ 1 の出力先 … 画面
⇒ 2 の出力先 … 画面
と何も変化しない。なぜなら、「2 の出力先を、1 の出力先と同じものに設定した」からである。その後に「1>file」が処理され、
⇒ 1 の出力先 … file
⇒ 2 の出力先 … 画面
となり、あなたの望んだものとは異なる結果となってしまう。

なお、「n>&m」は「n の出力先を、m の出力先と同じものに設定する」と説明してきたが、UNIX 的には「dup2(m,n)」しているにすぎない。

さて、最後に宿題。以下のシェルスクリプトは何を意図したものでしょうか?
#!/bin/sh
exec 3>&1
status=$({ { command1 3>&- 4>&-; echo $? 1>&4 3>&- 4>&-;} | command2 1>&3 3>&- 4>&-;} 4>&1)
if [ $status != 0 ]; then
...
fi
ちなみに bash であれば $PIPESTATUS を使えば一発で解決である。

csh・tcsh の場合
標準出力と標準エラー出力を両方リダイレクトしたい場合は、
% command >& file.txt
% command |& command2
とする。
% command &> file.txt (誤り!)
% command &| command2 (誤り!)
ではないことに注意。これだと、command をバックグラウンドで実行して、その標準出力をファイルや他のコマンドに渡すことになってしまう。

標準出力を file1 に、標準エラー出力を file2 に、などと振り分けるには、
% ( command > file1 ) >& file2
とする。これを応用して、標準エラー出力を全く表示させず、標準出力だけを見るには
% ( command > /dev/tty ) >& /dev/null
とすればよい。例えば、find コマンドで「他人のディレクトリを見る権限がない」という意味の Permission denied というエラー表示が邪魔なときは
% ( find / > /dev/tty ) >& /dev/null
とするとよい。

また、標準エラー出力だけをファイルに書き出すには下記のようにする。
% ( command > /dev/null ) >& file

標準出力だけをページャなどで見たい場合は、普通に
% command | less
として、less 実行中に Ctrl-l で画面を再描画すると、標準エラー出力が消えて見やすくなる。

csh・tcsh の文法は少々腐っているので、標準エラー出力が絡んでくると、トリッキーな技を使う必要がある。

関連コマンド
コマンド結果を画面とファイル両方に出力したい場合、tee コマンドを使うとよい。

リダイレクト時にバッファリングのために意図せぬ挙動になることがある。stdbuf を参照してほしい。