第8章 パイプとリダイレクト
Linuxのコマンドは「1つのことをうまくやる」小さな部品として作られており、それらを組み合わせることで複雑な処理を実現します。その組み合わせの要となるのが、パイプとリダイレクトです。
標準入力・標準出力・標準エラー出力
すべてのコマンドには、既定で3つの「窓口」が用意されています。
| 名称 | 略称 | 既定の行き先 |
|---|---|---|
| 標準入力 | stdin (0) | キーボードからの入力 |
| 標準出力 | stdout (1) | 画面への通常の出力 |
| 標準エラー出力 | stderr (2) | 画面へのエラーメッセージ出力 |
リダイレクトやパイプは、この窓口の行き先を「画面」から「ファイル」や「別のコマンド」に付け替える仕組みです。なぜ出力とエラーが別の窓口に分かれているかというと、通常の結果とエラーメッセージを別々に扱えるようにするためです。たとえば「正常な結果だけをファイルに保存し、エラーは画面に出したまま気づけるようにする」といった使い分けが可能になります。
リダイレクト(>, >>, <)
$ echo "hello" > out.txt # 出力をファイルへ書き込む(上書き)
$ echo "world" >> out.txt # 出力をファイルへ追記する
$ cat out.txt
hello
world
$ command 2> error.log # エラー出力だけをファイルへ
(commandの部分は実在するコマンド名に置き換えて使う。以下は具体例)
$ ls /nonexistent 2> error.log
$ cat error.log
ls: cannot access '/nonexistent': No such file or directory
この行のこの値に注目: ls /nonexistentを実行すると、対象が存在しないためエラーメッセージが標準エラー出力(2)に書き出されます。2> error.logでそのエラー出力だけをファイルへ逃がしているため、画面には何も表示されず、代わりにerror.logの中にエラーメッセージが記録されます。
> は上書き、>> は末尾への追記です。混同すると既存の内容を消してしまうので注意しましょう。特に「ログを溜めていきたいのに > を使ってしまい、実行するたびに前回までの内容が消えてしまった」という失敗はとてもよくあるので、追記したいときは>>であることを指差し確認するくらいの意識でいるとよいでしょう。
実機では、標準出力と標準エラー出力をまとめて1つのファイルに保存したい場合、command > all.log 2>&1(または新しめのシェルでは command &> all.log)という書き方も使われます。2>&1は「エラー出力(2)を、その時点の標準出力(1)と同じ行き先に向ける」という意味で、初心者には少し難しい記法ですが、ログ収集の場面でよく見かけるので名前だけでも覚えておくと役立ちます。
なぜ 2>&1 は「書く順番」で結果が変わるのか
先ほどの2>&1には、初心者がよくつまずくポイントがあります。それは、同じ内容に見えても、書く順番によって結果が変わるということです。標準出力(1)と標準エラー出力(2)は、それぞれ「今どこに向かって出力するか」という行き先の情報を持つ、番号の付いた郵便受けのようなものだとイメージしてください。>や2>&1といったリダイレクトの指定は、シェルによってコマンド行の左から右へ、書かれた順番どおりに1つずつ処理されます。そして2>&1が行っているのは、「2番の郵便受けの転送先を、今この瞬間の1番の転送先と同じ場所にコピーする」という、その場限りの操作です。あとから1番の転送先を変更しても、2番は自動的には追従しません。
パターンA: command > all.log 2>&1
① > all.log … まず1番(標準出力)の行き先を all.log に変更する
② 2>&1 … 次に2番(標準エラー出力)を「今の1番と同じ場所」にする=all.log
結果:1番も2番も all.log に書き込まれる(狙いどおり)
パターンB: command 2>&1 > all.log
① 2>&1 … まず2番を「今の1番と同じ場所」にする=この時点ではまだ画面
② > all.log … 次に1番の行き先だけを all.log に変更する
結果:1番は all.log、2番は画面のまま(①の時点でコピーした行き先が画面のままなので追従しない)
パターンBのように順番を逆にすると、標準エラー出力は画面に表示されたままになってしまいます。「同じ2つの記号を使っているのに結果が違う」という現象は、それぞれのリダイレクトが、書かれた時点での状態を見てその場でコピーしているだけという仕組みを知っていれば、混乱せずに正しい順番(先に>で1番の行き先を決めてから、2>&1で2番を1番に合わせる)を選べるようになります。
パイプ(|)
パイプはコマンドの出力を、次のコマンドの入力にそのままつなげます。
$ cat access.log | grep ERROR | wc -l
# access.log の中から ERROR を含む行を抜き出し、その行数を数える
2
この行のこの値に注目: access.logには通常のアクセス記録に混じってERROR:という文字列を含む行が2行あり、catでその全体を出力しgrepで絞り込み、wc -lで行数を数えた結果として最終的に2という数値だけが画面に表示されます。パイプでつないだ場合、途中の出力は画面に表示されず、最後のコマンド(この場合はwc -l)の結果だけが見える点に注目してください。
このように、シンプルなコマンドを | でいくつも連結することで、専用のプログラムを書かなくても複雑な処理を組み立てられます。
cat access.log | grep ERROR | wc -lでは3つのプロセスがパイプでつながり、途中のcatとgrepの出力は画面に表示されず、最後のwc -lの出力(2)だけが画面に表示されます。複数のパイプをつなげる
パイプは2つだけでなく、いくつでも連結できます。前段の出力を次々と絞り込んでいくイメージです。
$ cat access.log | grep -i error | head -n 5
# access.log からerrorを含む行を(大文字小文字区別せず)抜き出し、先頭5件だけ表示
203.0.113.11 - - [21/Aug/2026:10:03:40 +0900] "GET /api/orders HTTP/1.1" 500 89 ERROR: database timeout
203.0.113.11 - - [21/Aug/2026:10:05:15 +0900] "GET /api/orders HTTP/1.1" 500 91 ERROR: database timeout
$ cat access.log | grep -i error | wc -l
# 上と同じ絞り込みをした上で、該当した行数を数える
2
この行のこの値に注目: 該当する行がもともと2行しかないため、head -n 5で先頭5件を指定していても実際に表示されるのは2行だけです。件数を数えたwc -lの結果も同じく2で、両方の絞り込みが一致していることが確認できます。
1つのコマンドで全部をやろうとせず、シンプルなコマンドを1段ずつ積み重ねて確認しながら組み立てるのがコツです。途中でおかしな結果になったら、パイプを1つずつ外して、どの段階から想定と違う出力になっているかを確認すると原因を特定しやすくなります。パイプでつながれた複数のコマンドは、実は同時に(並行して)動作しています。前段の処理が全部終わってから後段が始まるわけではなく、前段が少し出力するたびに、後段がそれを受け取って処理を進めていく仕組みになっています。
実機でよく使われる定番の組み合わせに
sort(行を並べ替える)と uniq(連続する重複行をまとめる)があります。たとえば cat access.log | grep -i error | sort | uniq -c | sort -nr のように連結すると、「エラー内容ごとの発生回数を数えて、多い順に並べる」という集計処理をコマンド1行で実現できます。uniqは連続した重複しか検出できないため、先にsortで同じ内容の行を隣り合わせにしておく必要がある点がポイントです。
パイプでつまずきやすいポイント
- catを省略できる場面がある:
cat file | grep パターンは動きますが、実はgrep パターン fileのようにgrep自身にファイルを渡す書き方でも同じ結果になります。パイプは万能ですが、コマンド自身がファイルを直接受け取れる場合はそちらの方がシンプルです。 - パイプの前後にスペースを忘れる/余計に入れる:
cmd1|cmd2のようにスペースなしでも動きますが、可読性のためにもcmd1 | cmd2のようにスペースを空ける書き方が一般的です。 - 後段コマンドがエラーでも気づきにくい: パイプでつないだ場合、シェルの終了ステータスは基本的に最後のコマンドのものになります。途中のコマンドが失敗していても、最後さえ成功していれば見た目上は問題なく見えてしまうことがあるので注意しましょう。
リダイレクト記号のまとめ
| 記号 | 意味 |
|---|---|
> | 標準出力をファイルへ書き込む(上書き) |
>> | 標準出力をファイルへ追記する |
2> | 標準エラー出力だけをファイルへ書き込む |
< | ファイルの中身を標準入力として読み込ませる |
$ cat < memo.txt # memo.txtの中身を標準入力として渡し、catで表示する
買い物リスト
- 牛乳
- パン
- コーヒー豆
この行のこの値に注目: 表示される内容自体はcat memo.txtと引数で渡した場合と同じですが、この例ではmemo.txtの中身が標準入力(0番)としてcatに流し込まれている点が異なります。見た目の結果が同じでも、裏側の仕組みが違うことを意識しておくと、標準入力からしか読み込めないコマンドに出会ったときに混乱しません。
catのようにファイル名を直接引数として渡せるコマンドでは<を使う場面は少ないですが、標準入力からしかデータを受け取れないコマンドを使う場合には重要になります。「ファイルを引数として渡す」書き方と「標準入力として渡す」書き方の2通りがあることを知っておくと、コマンドのマニュアルを読んだときに戸惑いにくくなります。
コマンド > /dev/null のように /dev/null へリダイレクトすることで「画面にもファイルにも残さず捨てる」ことができます。/dev/nullは何を書き込んでも消えてしまう特殊なファイルです。エラーメッセージだけを表示させずに黙らせたい場合は コマンド 2> /dev/null、両方とも消したい場合は コマンド > /dev/null 2>&1 のように組み合わせます。
tee — 画面表示と保存を両立する
リダイレクトでファイルに保存すると、その内容は画面には表示されなくなります。「保存もしたいし、その場で結果も確認したい」という場合には、実機では tee というコマンドが使われます。teeはパイプで受け取った内容を画面に表示しつつ、指定したファイルにも書き込むという、名前の通り配管の「T字継手」のような働きをします。
$ cat access.log | grep -i error | tee errors.txt
# 画面にエラー行を表示しつつ、同じ内容をerrors.txtにも保存する
203.0.113.11 - - [21/Aug/2026:10:03:40 +0900] "GET /api/orders HTTP/1.1" 500 89 ERROR: database timeout
203.0.113.11 - - [21/Aug/2026:10:05:15 +0900] "GET /api/orders HTTP/1.1" 500 91 ERROR: database timeout
この行のこの値に注目: 画面に表示されているのはgrepが絞り込んだ2行で、teeを使っているためこれと全く同じ内容がerrors.txtというファイルにも同時に書き込まれています。>で保存すると画面には何も表示されませんが、teeなら「確認しながら保存する」ことが両立できます。
xargs — 出力を別コマンドの引数にする
パイプは「前段の出力を、後段コマンドの標準入力として渡す」仕組みですが、コマンドによっては標準入力ではなく「引数」としてファイル名などを受け取る必要があります。実機ではこのようなときに xargs というコマンドを使い、パイプで受け取った内容を後段コマンドの引数へ変換して渡します。たとえば find . -name "*.tmp" | xargs rm のように使うと、見つかった.tmpファイルをまとめてrmの引数として渡し、一括削除できます。強力な分、対象を間違えると危険な操作にもなるため、まずはxargs echoのように安全なコマンドで挙動を確認してから使うのがおすすめです。
受け取った値をコマンドの途中(先頭以外の位置)に挿入したい場合は、-I{}のようなプレースホルダー指定が便利です。find . -name "*.log" | xargs -I{} mv {} archive/とすると、見つかった各ログファイルを{}の位置に差し込みながらarchive/ディレクトリへ移動できます。xargs単体では引数を末尾に追加することしかできないため、途中に挿入したい場面ではこの書き方を覚えておくと応用が利きます。
cat access.log | grep ERROR > errors.txt のように、パイプで絞り込んだ結果をそのままファイルに保存することもできます。パイプとリダイレクトは自由に組み合わせて使えます。パイプは「コマンドとコマンドをつなぐ」、リダイレクトは「コマンドとファイルをつなぐ」という役割の違いを意識すると、どちらを使うべきか迷ったときに整理しやすくなります。
「小さな道具を組み合わせる」という設計思想の意味
本章で扱ったパイプとリダイレクトは、単なる便利な記号ではなく、Linux(の源流であるUnix)の設計思想そのものを体現する仕組みです。1つ1つのコマンドはあえて機能を絞り込み、その代わりに「他のコマンドと自由に組み合わせられる」ことを重視して作られています。これは、巨大で万能な1つのプログラムを作るよりも、小さな専門ツールを部品として組み合わせるほうが、テストしやすく再利用もしやすいという考え方に基づいています。この発想は現代のソフトウェア開発における「マイクロサービス」の考え方にも通じるものがあり、Linuxコマンドの学習は単なる操作暗記にとどまらない、設計思想を学ぶ機会でもあるのです。
手を動かす小課題
練習用ターミナルで、次の課題を実際に試してみましょう。答えは見出しをクリックすると表示されます。
課題1: echo "test" > out.txtで書き込み、echo "test2" >> out.txtで追記し、cat out.txtで結果を確認してみましょう。
答え: cat out.txtを実行すると、testとtest2の2行が表示されます。>で新規作成(上書き)し、>>で末尾に追記した結果です。
課題2: 同じout.txtにもう一度echo "overwritten" > out.txtを実行し、中身がどう変わるか確認してみましょう。
答え: cat out.txtで確認すると、中身はoverwrittenの1行だけになっています。>は既存の内容を消して上書きすることを、実際の結果で確認できます。
課題3: cat memo.txt | grep コーヒー | wc -lのように、3つのコマンドをパイプでつないでみましょう。
答え: catで全文を出し、grepでキーワードを含む行に絞り込み、wc -lでその行数を数える、という3段階の処理が1行でつながります。途中のcat・grepの出力は画面に表示されず、最後のwc -lの結果だけが表示される点も確認してみましょう。