標準入力・標準出力・標準エラー出力という3つのストリーム

Linuxのコマンドは、実行されると自動的に3つの入出力の通り道(ストリーム)を持ちます。それぞれにファイルディスクリプタと呼ばれる番号が割り当てられています。

ストリームファイルディスクリプタ番号既定の接続先役割
標準入力(stdin)0キーボードコマンドへの入力
標準出力(stdout)1画面(端末)コマンドの通常の出力結果
標準エラー出力(stderr)2画面(端末)エラーメッセージ・警告

標準出力と標準エラー出力は、どちらも既定では画面に表示されるため、通常の実行結果とエラーメッセージが混ざって見えます。これらを別々に、あるいはファイルへ切り替えるための仕組みがリダイレクトです。

リダイレクト:入出力先をファイルに切り替える

標準出力のリダイレクト:>と>>

記法効果
コマンド > ファイル標準出力をファイルへ書き込む(ファイルが既にあれば上書き)
コマンド >> ファイル標準出力をファイルへ追記する(既存の内容は残る)
ls -l > filelist.txt      # 結果をfilelist.txtに書き込む(上書き)
echo "追記内容" >> log.txt # log.txtの末尾に追記する

標準入力のリダイレクト:<

コマンド < ファイルとすると、キーボードからの入力の代わりに、指定したファイルの内容を標準入力として渡せます。

mail -s "件名" user@example.com < body.txt

標準エラー出力のリダイレクト:2>

標準エラー出力はファイルディスクリプタ番号2のため、2>でリダイレクトします。番号を省略した>は標準出力(1>と同じ)を指します。

find / -name "*.conf" 2> /dev/null    # エラーメッセージ(権限エラー等)だけを捨てる
find / -name "*.conf" > result.txt 2> error.txt   # 正常な結果とエラーを別々のファイルに分ける

/dev/nullは「書き込んだ内容をすべて捨てる」特殊なデバイスファイルで、不要な出力を読みやすく消す目的でよく使われます。

標準出力と標準エラー出力をまとめる:2>&1

標準出力と標準エラー出力の両方を同じファイルにまとめたい場合は、2>&1(「ファイルディスクリプタ2を、その時点のファイルディスクリプタ1と同じ向き先にする」という意味)を使います。

コマンド > すべてのログ.txt 2>&1

標準出力と標準エラー出力を同じファイルにまとめる場合、2>&1は> ファイルの後ろに書きます。リダイレクトは左から順に処理されるため、先に2>&1を書いてしまうと、その時点ではまだ1が画面を向いたままのため、標準エラー出力だけが画面に残ってしまいますコマンド 2>&1 > ファイルと書くと、実行順序の都合で標準エラー出力は画面に表示されたまま、標準出力だけがファイルに書き込まれてしまいます。試験でも頻出の引っかけポイントです。[1]。

なお、Bashなどでは&>ファイルという、標準出力と標準エラー出力を1つの記法でまとめてリダイレクトする省略記法も使えます。これはPOSIXで規定された記法ではないため、移植性が必要な場合は> ファイル 2>&1を使います。

パイプ:あるコマンドの出力を別のコマンドの入力につなぐ

パイプ(|)は、あるコマンドの標準出力を、別のコマンドの標準入力に直接つなぐ仕組みです。ファイルを経由せずに、複数のコマンドを連携させて処理できます。

ps aux | grep nginx        # psの出力をgrepに渡して絞り込む
cat access.log | sort | uniq -c | sort -nr   # 複数のコマンドを連鎖させる

パイプでつながれた各コマンドは、それぞれ独立したプロセスとして同時に実行されます。

出力を画面とファイルの両方へ同時に送る:tee

リダイレクト(>)は出力先をファイルに切り替えるため、画面には何も表示されなくなります。画面で確認しながら、同時にファイルにも保存したい場合はteeを使います。

teeは、標準入力の内容をそのまま標準出力(画面)に流しつつ、同時に指定したファイルへも書き込むコマンドです。

オプション効果
(オプション無し)指定したファイルを上書きする
-a指定したファイルに追記する(上書きしない)
long_running_command | tee output.log         # 画面表示とファイル保存を両方行う
long_running_command | tee -a output.log       # 追記する場合

パイプの途中に挟むこともでき、その場合はパイプの次のコマンドにも同じ内容がそのまま渡されます。

command1 | tee middle_result.log | command2    # 途中経過を保存しつつ、後続の処理も続ける

コマンドの出力を引数として使う:xargs

パイプ(|)は、あるコマンドの出力を次のコマンドの標準入力として渡しますが、rmやechoのように、標準入力ではなくコマンドライン引数としてファイル名等を受け取ることを前提としたコマンドもあります。xargsは、標準入力から受け取った内容を、後続のコマンドのコマンドライン引数に組み立てて実行するコマンドです。

echo "file1.txt file2.txt" | xargs rm    # rm file1.txt file2.txt として実行される
find . -name "*.tmp" -print0 | xargs -0 rm          # 見つかった.tmpファイルすべてをrmの引数として渡す
主なオプション効果
-n 個数1回のコマンド実行に渡す引数の個数の上限を指定する
-I 置換文字列指定した文字列を、標準入力から読んだ各行に置き換えてコマンドを組み立てる(1行ごとに1回コマンドを実行)
-0(--null)入力をヌル文字(\0)区切りとして扱う(ファイル名にスペースや改行を含む場合に安全)
-p実行前に、実行してよいか確認を求める
find . -name "*.log" -print0 | xargs -0 -n 1 sh -c 'mv "$1" /backup/' sh    # ファイル名にスペースや改行を含んでいても安全に処理する
printf '%s\n' file1 file2 file3 file4 | xargs -n 2 echo    # 2個ずつまとめて引数に渡す

findコマンド自体が持つ-execアクションでも同様のことができ、-exec コマンド {} +とすれば複数のファイル名を1回のコマンド実行にまとめられます。xargsは、標準入力から受け取った内容をコマンドライン引数として渡したい場合に便利ですfindの-execアクションについては、「基本的なファイル管理」の技術解説記事で扱っています。[2]。


補足

  1. コマンド 2>&1 > ファイルと書くと、実行順序の都合で標準エラー出力は画面に表示されたまま、標準出力だけがファイルに書き込まれてしまいます。試験でも頻出の引っかけポイントです。 ↩
  2. findの-execアクションについては、「基本的なファイル管理」の技術解説記事で扱っています。 ↩