パイプとリダイレクト入門 - データの流れを理解する

パイプとリダイレクト入門 - データの流れを理解する

この記事で学べること

  • 「データの流れ」 という考え方が分かる
  • |(パイプ)と > >>(リダイレクト)の違いが分かる
  • 「標準出力」「標準エラー出力」の使い分けができる
  • grepsort を組み合わせて 小さなコマンドを連結 できるようになる

結論(先に覚えるべき型)

  • 画面に出る結果を ファイルに保存 したい → > >>
  • 結果を 次のコマンドに渡す|
  • エラーだけ 別扱い → 2>

1. まずはここから:データはどこから来てどこへ行く?

リナ: 先輩、ls を実行すると画面にファイル名が出ますよね。あれってどこから出てるんですか?
ライニー先輩: いい質問だね。Linuxのコマンドは 「入力 → 処理 → 出力」 の3つの口を持っているんだ。図にするとこんな感じ。
ライニー先輩: 入力(標準入力 / stdin)→ コマンド → 出力(標準出力 / stdout)+ エラー(標準エラー出力 / stderr)
リナ: 入力と出力で口が分かれてるんですね。
ライニー先輩: そう。そして大事なのは、この口は付け替えできる ということ。画面に出してるものを、ファイルに切り替えたり、別のコマンドに渡したり。それが今日のテーマだよ。

3 つの口の名前

名前 略称 番号 デフォルトの行き先
標準入力 stdin 0 キーボード
標準出力 stdout 1 画面
標準エラー stderr 2 画面

2. リダイレクト:出力をファイルに保存する

2-1. > で上書き保存

$ ls > files.txt
リナ: 何も画面に出ません...失敗ですか?
ライニー先輩: 大丈夫。>画面ではなくファイルに行き先を切り替えた から、画面には出ないんだ。中身を見てごらん。
$ cat files.txt
Documents
Downloads
files.txt
Pictures

2-2. >> で追記

$ echo "1行目" > log.txt
$ echo "2行目" >> log.txt
$ cat log.txt
1行目
2行目

>既存の中身を消して上書き する。大事なファイルに > を使うと内容が消える。追記したいときは必ず >> を使うこと。

2-3. < で入力をファイルから取る(応用)

$ wc -l < log.txt
2
リナ: wc -l log.txt と何が違うんですか?
ライニー先輩: 結果はほぼ同じだけど、ファイル名がコマンドに渡らない のが違い。< は「キーボードの代わりにファイルを差し込む」イメージ。最初は >>> だけ覚えればOK。

3. パイプ:コマンドをつなぐ

3-1. 基本形

|(縦棒、パイプ)は 「左のコマンドの出力を、右のコマンドの入力にする」 記号。

$ ls | wc -l
12
リナ: ls の結果を wc -l に渡してファイル数を数えてるんですね!
ライニー先輩: その通り。これがパイプの威力。1 つのコマンドで全部やる必要はなくて、小さなコマンドを連結して目的を達成する のがLinuxの流儀だよ。

3-2. よく使う組み合わせ

# ファイル一覧から「.txt」を含むものだけ抽出
$ ls | grep .txt

# プロセス一覧から nginx を含む行を抽出
$ ps aux | grep nginx

# ログを新しい順に表示
$ cat access.log | sort -r | head -n 10

パイプは何個でもつなげる

$ コマンド1 | コマンド2 | コマンド3 | ...

各段で「絞り込む」「並び替える」「整形する」と役割を分担させるのがコツ。

4. リダイレクトとパイプの違い

リナ: 結果をどこかに送るのは同じに見えるんですけど、>| って何が違うんですか?
ライニー先輩: 行き先が違うんだ。図にするね。

>| の違い

cmd > file    →  出力先が【ファイル】
cmd | cmd2    →  出力先が【次のコマンドの入力】
  • >:保存したい時
  • |:もう一段加工したい時
# パターンA: ls の結果をファイルに保存
$ ls > list.txt

# パターンB: ls の結果を grep で絞り込む
$ ls | grep ".log"

# パターンC: 組み合わせ。grep で絞った結果をファイルに保存
$ ls | grep ".log" > log-files.txt

| でつないでから最後に > でファイル化、というのが実務で一番多いパターン。

5. 標準エラー出力(stderr)の扱い

5-1. エラーは別の口から出ている

$ ls /not-exist > out.txt
ls: '/not-exist' にアクセスできません: そのようなファイルやディレクトリはありません
リナ: あれ? > out.txt でファイルに出したつもりなのに、エラーが画面に出てます...
ライニー先輩: それは エラーは標準エラー出力(stderr)から出ていて、> は標準出力(stdout)しか拾わない から。エラーを拾うには番号 2 を指定するんだ。

5-2. エラーを別ファイルに分ける

$ ls /not-exist 2> error.log
(画面には何も出ない)
$ cat error.log
ls: '/not-exist' にアクセスできません: そのようなファイルやディレクトリはありません

5-3. 通常出力とエラーをまとめて1ファイルに

$ コマンド > all.log 2>&1

2>&1 の意味

標準エラー(2)を、標準出力(1)と同じ場所に流す」という指示。 ログ収集で頻出する書き方なので、形で覚えてしまうのがおすすめ。

書く順番に注意。2>&1 > all.log ではなく > all.log 2>&1 の順序が正しい。逆にすると stderr がリダイレクト前の場所(画面)に流れてしまう。

6. よくある初心者のつまずき

6-1. > を使ったらファイルが空になった

$ cat important.txt > important.txt   # NG: ファイルが空になる

>コマンド実行前にファイルを空にする 動きをする。cat important.txt が読む前に中身が消えている。 自分自身に向けてリダイレクトしない のが鉄則。

6-2. | の前後にスペースが要る? 要らない?

どちらでも動く。ただし 読みやすさのため前後にスペースを入れる のが慣習。

$ ls|grep txt       # OK だが見づらい
$ ls | grep txt     # 推奨

6-3. パイプの途中の結果が見たい

$ ls | tee list.txt | wc -l

tee流れている内容をファイルに記録しつつ、そのまま次に渡す コマンド。デバッグや「途中経過も保存しておきたい」ときに便利。

7. ミニ課題:実際にやってみよう

リナ: 知識は入りました! 実際に手を動かしてみたいです。
ライニー先輩: いいね、3問用意したよ。ターミナルで試してみて。

課題1: 自分のホームディレクトリのファイル一覧を home-files.txt に保存しよう。

ヒントを見る

> でファイルに書き出す。

解答例
$ ls ~ > home-files.txt

課題2: /etc 以下のファイル数を数えて画面に表示しよう(パイプを使う)。

ヒントを見る

ls の結果を wc -l に渡す。

解答例
$ ls /etc | wc -l

課題3: /etc の中で .conf で終わるファイルだけ取り出して、conf-list.txt に保存しよう。

ヒントを見る

lsgrep> の3段構え。

解答例
$ ls /etc | grep "\.conf$" > conf-list.txt

grep "\.conf$"$ は「行末」を意味する正規表現。「.conf で終わる行」だけが残る。

8. コピペ用テンプレート

よく使う型をまとめておく

# 結果をファイルに保存(上書き)
コマンド > out.txt

# 結果をファイルに追記
コマンド >> out.txt

# 結果を絞り込む
コマンド | grep キーワード

# 結果を並び替えて先頭10件
コマンド | sort | head -n 10

# 通常出力とエラーをまとめて記録
コマンド > all.log 2>&1

# エラーだけ別ファイルに
コマンド 2> error.log

# 途中経過もファイルに残しつつ次へ
コマンド | tee progress.txt | 次のコマンド

まとめ:次に読む