本記事には広告(アフィリエイトリンク)が含まれます。

リダイレクトとパイプ LinuC101 第9回

広告

新卒インフラエンジニア向けLinuC 101 試験対策シリーズの第9回です。前回はファイルへの権限を整える話まで来ました。今回は コマンドの出力をどうつなぐか ── リダイレクト、パイプ、そしてシェルそのものの機能を扱います。

「Linux はコマンドラインの強さで生きている」と先輩から言われたら、その正体はこの章で扱う パイプ です。ls | wc -l のような小さな組み合わせから、ログ調査・自動化・障害対応まで、すべてが「コマンドの口をつなぐ」発想で動いています。

環境前提

  • ディストリビューション: AlmaLinux 9.6(Sage Margay)
  • シェル: bash 5.1.8(既定 alias ls --color=auto 等が定義済み)
  • ユーザー: developer(sudo NOPASSWD設定済み)
  • 関連VM: linuc-alma(10.0.10.132)
  • 演習用ディレクトリ ~/linuc-cli/ を作成 → 末尾でクリーンアップ
広告

今ここマップ

LinuC 101 試験対策シリーズ(全12回)

  第1回 Linuxの起動・接続・停止
  第2回 ブートプロセスの仕組み
  第3回 systemdマスター講座
  第4回 プロセス管理とハードウェア基礎
  第5回 仮想マシンとコンテナの基礎
  第6回 パッケージ管理マスター
  第7回 ファイル操作の実践
  第8回 パーミッション・所有者・特殊権限
▶ 第9回 コマンドライン・リダイレクト・パイプ    ← いまここ
  第10回 テキスト処理(grep / sed / awk)
  第11回 vi/vim 入門
  第12回 ディスク・パーティション・ファイルシステム

この記事で身につくこと

  1. 標準入力(fd 0)/ 標準出力(fd 1)/ 標準エラー出力(fd 2)の概念を説明できる
  2. > / >> / < / 2> / 2>&1 / &> / /dev/null を使い分けられる
  3. |(パイプ)でコマンドを連結し、tee で分岐、xargs で引数化できる
  4. env / export で環境変数を確認・設定でき、alias でショートカットを作れる
  5. history / ! 表記 / Tab 補完など対話シェルの基本機能を使える

第1章:プロセスは3つの口を持つ

Linux のあらゆるコマンド(プロセス)は、起動した瞬間から 3つの口 を開きます。

プロセスの3つの口(標準入出力とfd)の概念図。中央のプロセスボックスから、左側のキーボードからfd 0(stdin、青)で入力、右側の端末(画面)へfd 1(stdout、緑)とfd 2(stderr、赤)で出力。リダイレクト・パイプはこれらの接続先を「つなぎ替える」操作
図 09-01. プロセスの3つの口 ── 標準入出力と fd

「fd」は file descriptor(ファイル記述子) の略。プロセスから見ると、入出力先は番号で識別されるただのチャンネルです。標準では端末につながっていますが、このチャンネルを別の場所につなぎ替えるのがリダイレクトとパイプです。

なぜ標準出力と標準エラーを分けているのか

「正常な結果」と「エラーメッセージ」を別ストリームにすることで、片方だけをファイルに保存したり、片方だけを破棄したりできます。cron で動く自動化スクリプトを考えてみてください。標準出力には集計結果、標準エラーには警告だけが出るように設計しておくと、2> /var/log/myjob.err でエラーだけ別ログに分離できます。

第2章:リダイレクト ── 向きを切り替える

まず作業ディレクトリを作ります。linuc-almaで実行:

$ rm -rf ~/linuc-cli
$ mkdir -p ~/linuc-cli

2.1 標準出力リダイレクト(> と >>)

linuc-almaで実行:

$ echo line1 > ~/linuc-cli/file1.txt
$ echo line2 >> ~/linuc-cli/file1.txt
$ cat ~/linuc-cli/file1.txt

実行結果:

line1
line2
  • >:標準出力をファイルに書く(既存ファイルは上書き。中身は消える)
  • >>:標準出力をファイルに 追記する(既存内容を残して末尾に追加)

>>> の取り違えはサーバー事故の常連。設定ファイルに > で書こうとして元の中身を全部消した、という事例は後を絶ちません。第9章でヒヤリハットとして扱います。

2.2 標準入力リダイレクト(<)

linuc-almaで実行:

$ wc -l < ~/linuc-cli/file1.txt

実行結果:

2

wc -l file でも同じ結果になりますが、< を使うと「ファイル名は出力されず数字だけ」という違いがあります(wc は引数からのファイルパスを表示するが、標準入力経由だと表示しない)。スクリプトで「数字だけ欲しい」場面で重宝します。

2.3 標準エラー出力リダイレクト(2>)

fd 2 のリダイレクトには 2> と書きます。linuc-almaで実行:

$ ls /etc/passwd /no-such-file 2> ~/linuc-cli/err.txt

実行結果(端末には標準出力だけ出る):

/etc/passwd

linuc-almaで実行:

$ cat ~/linuc-cli/err.txt

実行結果:

ls: '/no-such-file' にアクセスできません: そのようなファイルやディレクトリはありません

エラーだけが err.txt に書かれ、正常な結果は端末に流れる。これが標準出力と標準エラーを分けている恩恵です。

2.4 標準出力+エラーをマージ(2>&1 と &>)

2>&1 は「fd 2 を fd 1 と同じ場所に向ける」という意味です。& は「fd 番号の指定」を表します。linuc-almaで実行:

$ ls /etc/passwd /no-such-file > ~/linuc-cli/all.txt 2>&1
$ cat ~/linuc-cli/all.txt

実行結果:

ls: '/no-such-file' にアクセスできません: そのようなファイルやディレクトリはありません
/etc/passwd

標準出力とエラーが両方ファイルに入りました。bash には拡張記法 &> があり、こちらの方が短く書けます。linuc-almaで実行:

$ ls /etc/passwd /no-such-file &> ~/linuc-cli/all2.txt
$ cat ~/linuc-cli/all2.txt

実行結果:

ls: '/no-such-file' にアクセスできません: そのようなファイルやディレクトリはありません
/etc/passwd

同じ結果。2>&1 が POSIX 標準、&> が bash 拡張。スクリプトの移植性を考えるなら 2>&1、対話的な打ち込みなら &> が短くて便利。

2.5 順序の罠

リダイレクトは 左から右に処理されます。これを知らないと事故ります。

  • command > file 2>&1 :fd 1 を file に向ける → fd 2 を fd 1(=file)に向ける → 両方 file に書かれる
  • command 2>&1 > file :fd 2 を fd 1(=端末)に向ける → fd 1 を file に向ける → fd 1 だけが file に書かれ、fd 2 は端末に出る

順序が違うだけで結果が変わるので、2>&1 は最後に書く」と覚えておくのが安全です。

2.6 /dev/null ── 出力を捨てる

/dev/null は「書き込んでも何も起きない、読み込んでも何もない」という特殊なデバイスファイル。出力を捨てたい時に使います。

linuc-almaで実行:

$ ls /no-such-file 2> /dev/null

実行結果(何も出ない):

(出力なし)

エラーだけ消したいときの定番。> /dev/null 2>&1 なら両方捨てる(cron ジョブで「成功時は何も言うな」というケース)。

📖 試験Tipsボックス:リダイレクト記号

主題:1.03.3(重要度:高)
出題パターン:「標準エラーをファイルに書くには?」「標準出力と標準エラーを同じファイルにマージは?」「上書きと追記の違いは?」

暗記ポイント

  • > 上書き / >> 追記 / < 入力
  • 2> エラーリダイレクト / 2>&1 エラーを標準出力にマージ
  • &> bash 拡張で両方一括
  • 2> /dev/null エラーを捨てる / > /dev/null 2>&1 両方捨てる
  • 順序:> file 2>&1 はOK、2>&1 > file は NG

第3章:パイプ ── Unix 哲学の核

パイプ(|)の本質はとてもシンプル。左コマンドの標準出力を、右コマンドの標準入力につなぐ。それだけです。

linuc-almaで実行:

$ ls /etc | wc -l

実行結果:

172

ls /etc が出した一覧(標準出力)が、wc -l の標準入力に流れ込み、行数が数えられました。/etc 内のファイル数は導入済みパッケージの累積で前後するため、手元では172前後の値になります。

linuc-almaで実行:

$ cat /etc/passwd | grep developer

実行結果:

developer:x:1000:1000:developer:/home/developer:/bin/bash

cat が出力した /etc/passwd の中身を grep がフィルタしています。

3.1 多段パイプは Unix の作法

パイプはいくつでもつなげられます。「小さな道具を組み合わせる」のが Unix の哲学。

$ dmesg | grep -i error | head -5
(エラーログの最初の5件だけ抽出)

各コマンドはそれぞれ「ひとつの仕事」をしています。組み合わせ方は無限。新しい問題に直面したとき、「どんなパイプの組み合わせで解決できるか」を考える発想が、コマンドラインで戦うエンジニアの思考様式です。

3.2 標準エラーはパイプを通らない

パイプは 標準出力(fd 1)だけを次のコマンドに渡します。標準エラー(fd 2)はそのまま端末に出ます。エラーもまとめてフィルタしたい場合は、先に 2>&1 でマージしてからパイプ:

$ command 2>&1 | grep something
(または bash の場合は |& が同等)

📖 試験Tipsボックス:パイプ vs リダイレクト

主題:1.03.3(重要度:高)
出題パターン:「|> の違いは?」「標準エラーはパイプを通る?」「ls | wc -l の動作は?」

暗記ポイント

  • パイプ = コマンド → コマンド(左の stdout を右の stdin に)
  • リダイレクト = コマンド ↔ ファイル
  • 標準エラーはパイプを通らない。マージしたいなら 2>&1 |
  • 多段パイプで「小さな道具を組み合わせる」のが Unix 哲学

第4章:tee と xargs ── パイプの仲間

4.1 tee ── 出力を分岐

tee は配管の T字管が由来。標準入力を受け取って ファイルにも書きつつ、標準出力にも流す。「途中経過を保存しながらパイプを継続したい」ときの定番。

linuc-almaで実行:

$ ls -l /etc | tee ~/linuc-cli/etc.txt | wc -l

実行結果:

173

linuc-almaで実行:

$ head -3 ~/linuc-cli/etc.txt

実行結果:

合計 1056
-rw-r--r--.  1 root root     4673  3月 12  2025 DIR_COLORS
-rw-r--r--.  1 root root     4755  3月 12  2025 DIR_COLORS.lightbgcolor

ls -l /etc の出力が etc.txt に保存されつつ、wc -l にも流れて 173 行と数えられました(行数は環境により前後します)。追記したいなら tee -a(上書きせずに末尾に追加)。

tee の典型用途:command | tee output.log | grep ERROR(全部保存しつつエラーだけ画面に)、echo "..." | sudo tee /etc/conf(root 権限が必要なファイルへのリダイレクト)。

4.2 xargs ── 標準入力を引数に

パイプは「標準入力」として受け取ります。しかし、cprm のように引数を取るコマンドにパイプで渡したい場合があります。これを変換するのが xargs

linuc-almaで実行:

$ printf '1\n2\n3\n' | xargs -I{} echo 'num: {}'

実行結果:

num: 1
num: 2
num: 3

-I{} は「{} をプレースホルダにして、入力1行ごとにコマンドを実行」。{} は別の文字列でも構いません(-I% なら %)。

linuc-almaで実行:

$ find ~/linuc-cli -type f | xargs wc -l

実行結果:

    2 /home/developer/linuc-cli/file1.txt
    1 /home/developer/linuc-cli/err.txt
    2 /home/developer/linuc-cli/all.txt
    2 /home/developer/linuc-cli/all2.txt
  173 /home/developer/linuc-cli/etc.txt
  180 合計

find がファイル一覧を出力 → xargs がそれを wc -l の引数に変換 → 各ファイルの行数を一括カウント。

注意:ファイル名にスペースや改行が含まれる場合は find -print0 | xargs -0 という NUL 区切り版を使います。本番ファイル名にスペースが混じると静かに事故るので、自動化スクリプトでは -print0 派が標準。

第5章:環境変数とシェル変数

5.1 シェル変数と環境変数の違い

変数には2種類あります。

  • シェル変数:現在のシェルだけが知っている変数。子プロセスには引き継がれない
  • 環境変数:シェル変数を export したもの。子プロセス(起動するコマンド)に引き継がれる

これを実機で確認します。linuc-almaで実行:

$ MY_VAR=local
$ echo "MY_VAR=$MY_VAR"

実行結果:

MY_VAR=local

linuc-almaで実行:

$ export MY_GLOBAL=exported
$ echo "MY_GLOBAL=$MY_GLOBAL"

実行結果:

MY_GLOBAL=exported

では子プロセス(新しい bash)から両方の変数が見えるか確認。linuc-almaで実行:

$ bash -c 'echo "child: MY_VAR=[$MY_VAR] MY_GLOBAL=[$MY_GLOBAL]"'

実行結果:

child: MY_VAR=[] MY_GLOBAL=[exported]

子プロセスでは MY_VAR が空になり、MY_GLOBAL だけが見える。これが export の決定的な違いです。スクリプトを実行する際、外部のコマンドに変数を渡したいなら export 必須。

5.2 環境変数の確認

コマンド表示する内容
env環境変数のみ(exportされたもの)
setシェル変数すべて(環境変数も含む)+関数定義
echo $VAR個別の変数の値
printenv VAR環境変数 VAR の値(無ければ exit 1)
export(引数なし)環境変数の一覧(declare -x 形式)

linuc-almaで実行:

$ env | grep -E '^(SHELL|HOME|USER|LANG|PWD|LOGNAME|TERM)='

実行結果:

SHELL=/bin/bash
PWD=/home/developer
LOGNAME=developer
HOME=/home/developer
LANG=ja_JP.UTF-8
TERM=xterm-256color
USER=developer

5.3 主要な環境変数

変数意味
PATH実行ファイルを検索するディレクトリのリスト(コロン区切り)
HOME現ユーザーのホームディレクトリ
USER / LOGNAME現ユーザー名
SHELLログインシェルのパス
PS1プライマリプロンプトの定義(カスタマイズ可能)
LANG / LC_*言語・ロケール設定
TERM端末種別(vimless の表示に影響)
EDITOR既定のエディタ(visudo 等が参照)

削除には unset VAR。永続化したい場合は ~/.bashrc~/.bash_profileexport を書きますが、これは LinuC 102 試験範囲(次のシリーズ)で詳しく扱います。

📖 試験Tipsボックス:環境変数

主題:1.03.1(重要度:高)
出題パターン:「環境変数を子プロセスに渡すコマンドは?」「環境変数だけを表示するコマンドは?」「シェル変数と環境変数の違いは?」

暗記ポイント

  • VAR=value:シェル変数(子に伝わらない)
  • export VAR=value:環境変数(子に伝わる)
  • env:環境変数のみ表示 / set:シェル変数全部 / printenv VAR:個別
  • unset VAR:削除
  • 主要:PATH HOME USER SHELL PS1 LANG TERM EDITOR

第6章:エイリアスと履歴

6.1 alias ── ショートカット

長いコマンドや組み合わせに別名を付けるのが alias。linuc-almaで実行:

$ alias ll='ls -alF'
$ type ll

実行結果:

ll は `ls -alF' のエイリアスです

linuc-almaで実行:

$ alias | head -5

実行結果:

alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -alF'

AlmaLinux にはログイン時から ls --color=autogrep --color=auto といった alias が 既定で定義されています。/etc/profile.d/ 配下のスクリプトが読み込まれた結果です。

削除は unalias。linuc-almaで実行:

$ unalias ll
$ type ll

実行結果:

bash: type: ll: 見つかりません

「一時的に alias を回避したい」場合は \ls のようにバックスラッシュを前置します。これで ls 本体(/usr/bin/ls)が呼ばれ、エイリアスは適用されません。

6.2 type ── そのコマンドの正体

type コマンド名 で、それが alias / シェル組み込み / ファイル / 関数 のどれかが分かります。

  • type ls → 「ls は `ls –color=auto’ のエイリアスです」
  • type cd → 「cd はシェル組み込みです」
  • type cat → 「cat は /usr/bin/cat です」

which$PATH から実行ファイルだけを探しますが、type は組み込みや alias まで全部見つけてくれます。第7回でも which がエイリアスを併記する挙動を見ましたね。「これは alias だっけ? 組み込みだっけ?」を調べるなら type です。

6.3 history ── 過去のコマンド

対話シェルは過去に打ったコマンドを記録しています。linuc-almaで実行:

$ history | tail -5

実行結果(環境ごとに異なる):

   26  echo "##UNALIAS##"
   27  unalias ll
   28  type ll 2>&1
   29  echo "##HISTORY##"
   30  history | tail -5
記法意味
!!直前のコマンドを再実行
!N履歴のN番目を再実行(例:!28
!str直近の str で始まるコマンドを再実行(例:!ls
Ctrl+R履歴をインクリメンタル検索(reverse-i-search)
history -c履歴をクリア
history -d N履歴の N 番目を削除

履歴ファイルの場所は ~/.bash_history。シェル終了時にメモリ上の履歴が書き出されます。パスワード等の機密が含まれた場合は history -d N で消すのが基本マナー。

6.4 補完 ── Tab キーは友達

  • Tab:途中まで入力した名前を補完(コマンド名、ファイル名、変数名)
  • Tab Tab:候補が複数ある場合に一覧表示
  • bash-completion パッケージが入っている AlmaLinux では、git ch<Tab>git checkout のようなサブコマンド補完も効く

📖 試験Tipsボックス:alias と history

主題:1.03.1(重要度:高)
出題パターン:「直前のコマンドを再実行する記法は?」「alias 一覧表示は?」「コマンドの種別を調べるのは?」

暗記ポイント

  • alias(一覧)/ alias name='cmd'(定義)/ unalias name(削除)
  • \cmd で alias を一時的にバイパス
  • !! 直前 / !N N番目 / !str str で始まる直近 / Ctrl+R 検索
  • history 一覧 / history -c 全消去 / history -d N 個別削除
  • type cmd でコマンドの種別(alias / builtin / file)を確認

第7章:実用ワンライナー集

ここまでの道具を組み合わせると、現場で頻出する作業がワンライナーで片付きます。

  • 直近1時間のサービスエラー抽出journalctl --since "1 hour ago" | grep -i error
  • 設定ファイルからコメント・空行を除外grep -v '^\s*#' /etc/ssh/sshd_config | grep -v '^$'
  • ディスク使用量Top10du -h /var | sort -h | tail -10
  • 動作中プロセス数ps -ef | wc -l
  • ファイル数カウントfind . -type f | wc -l
  • ログイン中ユーザー一覧(重複なし)who | awk '{print $1}' | sort -u
  • 巨大ログだけ削除find /var/log -size +100M | xargs -r ls -lh-r は入力が空なら実行しない)

grepawk は次回(第10回)で本格的に扱います。本記事ではパイプの組み合わせ感だけ覚えておけば十分です。

第8章:現場での使いどころ

  • 障害ログの素早い絞り込みjournalctl -u nginx | grep -i error | tail -20 で「nginx の最近のエラーだけ」を切り出す
  • 監査用の出力保存dnf history > /tmp/dnf-$(date +%F).txt 2>&1 で日付付きスナップショット
  • ロケールを一時的に切り替えLANG=C ls -l で英語環境のメッセージを取得(自動化スクリプトで安定した出力が欲しいときの定番)
  • typo 防止 aliasalias rm='rm -i'~/.bashrc に。確認プロンプトを習慣化(ただし「alias で守られている」前提のスクリプトは別環境で事故るので注意)
  • パイプで前処理:「Excel に貼る前にコマンドラインで前処理」── ログを grep で絞り、awk で列抽出、sort -u で重複除去
  • 多段一括処理find /backup -name '*.tar.gz' -mtime +90 | xargs -r rm で90日より前のバックアップ削除
  • 履歴で監査history | grep sudo で過去の特権操作を確認。インシデント対応の初動で打つ定番

第9章:ヒヤリハット ── 同名ファイルへの>

⚠️ grep ERROR < log.txt > log.txt でファイルが空になる

新人I君は、log.txt から ERROR を含む行だけ抜き出して同じファイルに書き戻そうと、grep ERROR < log.txt > log.txt と打ちました。実行後、log.txt は空になりました

原因はシェルの仕組み:シェルは grep を起動する前にリダイレクトの解決を行い、> log.txt でファイルを開く瞬間にサイズ 0 に切り詰めます。その後 grep< log.txt(既に空)から読もうとするので、何も出力されない → 空のファイルが出来上がる、という流れです。

正しい書き方

  • 別ファイルに出してから差し替える:grep ERROR log.txt > log.txt.new && mv log.txt.new log.txt
  • spongemoreutils パッケージ)を使う:grep ERROR log.txt | sponge log.txt
  • noclobber オプション:set -o noclobber~/.bashrc に書くと、> での既存ファイル上書きが拒否される(強制したい時は >|

類似事例command > sshd_config で sshd_config を空にしてしまい SSH デーモンが起動拒否、リモート作業中に切断されたエンジニアが慌てて物理アクセス … という事例も多数。本番ファイルへの > は実行前にバックアップが鉄則です。

やってみよう

linuc-alma にログインしている前提で、演習1〜4を順に実行してください。

演習1:標準入出力とリダイレクト

  1. rm -rf ~/linuc-cli && mkdir -p ~/linuc-cli
  2. echo line1 > ~/linuc-cli/file1.txt(上書き)
  3. echo line2 >> ~/linuc-cli/file1.txt(追記)
  4. cat ~/linuc-cli/file1.txt(2行確認)
  5. wc -l < ~/linuc-cli/file1.txt(標準入力経由で行数)
  6. ls /etc /no-such > ~/linuc-cli/all.txt 2>&1(マージ)
  7. cat ~/linuc-cli/all.txt(標準出力+エラー両方が入っている)

演習2:パイプと tee と xargs

  1. ls /etc | wc -l(172 程度)
  2. cat /etc/passwd | grep developer
  3. ls -l /etc | tee ~/linuc-cli/etc.txt | wc -l(173)
  4. head -3 ~/linuc-cli/etc.txt(tee の保存先確認)
  5. printf '1\n2\n3\n' | xargs -I{} echo "num: {}"
  6. find ~/linuc-cli -type f | xargs wc -l

演習3:環境変数とシェル変数

  1. echo "$PATH"
  2. env | grep -E '^(SHELL|HOME|USER|LANG)='
  3. MY_VAR=local
  4. export MY_GLOBAL=exported
  5. bash -c 'echo "child: MY_VAR=[$MY_VAR] MY_GLOBAL=[$MY_GLOBAL]"'
  6. 子プロセスで MY_VAR が空になっていることを確認
  7. unset MY_VAR MY_GLOBAL

演習4:alias / type / history

  1. alias ll='ls -alF'
  2. type ll(alias 表示)
  3. type ls(既定 alias 表示)
  4. alias | head -5
  5. unalias ll
  6. type ll(見つかりません)
  7. history | tail -5
  8. クリーンアップrm -rf ~/linuc-cli

分からないオプションは man bash(REDIRECTION セクション)、help exporthelp history で調べる習慣を身につけてください。help はシェル組み込みコマンド用のヘルプ表示です。

理解度チェック

○か×で答えてください。回答と解説は次回冒頭で振り返ります。

  1. 標準出力のファイルディスクリプタ番号は 1、標準エラー出力は 2 である。
  2. command > file 2>&1command 2>&1 > file は同じ結果になる。
  3. パイプ | は左コマンドの標準出力と標準エラー出力の両方を右コマンドの標準入力に渡す。
  4. VAR=value で定義したシェル変数は、bash -c '...' で起動した子プロセスからも参照できる。
  5. !! は直前に実行したコマンドを再実行するシェル機能である。

解答

  • 1.  fd 0=stdin、fd 1=stdout、fd 2=stderr
  • 2. × 順序が違うと結果が違う。前者は両方 file に、後者は fd 1 だけ file・fd 2 は端末
  • 3. × パイプは標準出力(fd 1)のみ。標準エラーをマージしたいなら 2>&1 |
  • 4. × シェル変数は子プロセスに伝わらない。export して環境変数にする必要がある
  • 5.  !! は直前のコマンドを再実行する bash の history 展開機能

次回予告

第10回は「テキスト処理の三種の神器:grep / sed / awk + 正規表現」です。本記事のパイプの仕組みを土台に、3つのツールでテキストの抽出・置換・整形を扱います。grep でフィルタ、sed で置換・抽出、awk で列指向処理。これに正規表現を組み合わせると、ログ解析の効率が一気に上がります。

LinuC 101 試験対策シリーズ 全12回

広告
Linux
スポンサーリンク