【Shell Script】パイプとリダイレクトでコマンドをつなげる

Shell Script

こんにちは、かつコーチです。

「1つのコマンドの結果を、別のコマンドに渡して加工したい」「実行結果をファイルに残しておきたい」。
シェルスクリプトの真価は、複数のコマンドを組み合わせられる点にあります。
この記事では、コマンド同士をつなげるパイプと、出力先を切り替えるリダイレクトの使い方を解説します。
標準入出力の仕組み(09-hyoujun-nyushutsuryoku-shikumi)を前提に、実際の組み合わせ方を中心に見ていきます。

パイプ・リダイレクトとは?

パイプ=ベルトコンベアで次工程に渡す

パイプ|)とは、あるコマンドの標準出力を、別のコマンドの標準入力にそのままつなぐ仕組みです。

工場のベルトコンベアをイメージするとわかりやすいです。
1つの工程(コマンド)が完成させた部品(出力データ)を、そのまま次の工程(次のコマンド)に流し込み、さらに加工を加えます。
command1 | command2と書けば、command1の出力がcommand2の入力として自動的に流れていきます。

リダイレクト=出力先の差し替え

リダイレクト>>><)とは、コマンドの出力先や入力元を、通常の画面表示やキーボードから別の場所(主にファイル)に差し替える仕組みです。

普段コマンドの実行結果は画面に表示されますが、リダイレクトを使うと「画面ではなくファイルに書き出す」ように出口を差し替えられます。
郵便物の転送届のようなもので、「本来の届け先(画面)」を「別の届け先(ファイル)」に一時的に変更するイメージです。

基本の書き方

パイプでコマンドをつなぐ

psコマンドの出力から、特定のプロセスだけをgrepで絞り込む例です。

#!/bin/bash

# 実行中のプロセス一覧からnginxに関する行だけを抽出する
ps aux | grep nginx

パイプは複数連結することもできます。

#!/bin/bash

# ログファイルからERRORを含む行を抽出し、件数を数える
cat app.log | grep "ERROR" | wc -l

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

>は出力先のファイルを新規作成(上書き)し、>>は既存の内容の末尾に追記します。

#!/bin/bash

# 上書き:実行するたびに中身がリセットされる
echo "処理を開始しました" > log.txt

# 追記:実行するたびに末尾に行が追加される
echo "処理が完了しました" >> log.txt

<でファイルの中身を入力として渡す

#!/bin/bash

# name.txtの中身を標準入力として渡す
while read -r line
do
  echo "名前: $line"
done < name.txt

よくあるつまずきポイント・エラー対処

>で上書きしてログを消してしまう

日次の処理結果を1つのログファイルにまとめるつもりで、次のスクリプトをcronに登録していたことがあります。

❌Before(つまずいた実例)

#!/bin/bash

# 毎日この処理が実行される想定だった
echo "$(date): 処理を実行しました" > /var/log/daily.log

数日後にログを確認すると、直近1回分の実行結果しか残っておらず、それまでの履歴がすべて消えていました。

✅After(解決方法)

原因は、>が実行のたびにファイルを新規作成(上書き)する記号だったことです。
「追記」のつもりで使ってしまい、過去の実行履歴を毎回リセットしていました。
>>に書き換えたことで、実行のたびに履歴が積み重なるようになりました。

#!/bin/bash

echo "$(date): 処理を実行しました" >> /var/log/daily.log

>は上書き、>>は追記」という違いは知識としては単純ですが、ログ運用では取り返しのつかないデータ消失につながるため、書く前に必ず意識するようにしています。

応用・一歩先の使い方

teeで画面表示とファイル保存を両立する

teeコマンドを使うと、標準出力の内容を画面に表示しながら、同時にファイルにも保存できます。

#!/bin/bash

# 画面に結果を表示しつつ、result.logにも保存する
./build.sh | tee result.log

パイプの途中にteeを挟めば、処理の流れを止めずに途中経過を記録できます。

#!/bin/bash

# 抽出結果を画面表示しつつファイルにも残し、さらに件数も数える
cat access.log | grep "404" | tee not-found.log | wc -l

xargsでパイプの出力を引数として渡す

パイプは標準入力にデータを流しますが、コマンドによっては引数として値を受け取る必要があります。
そんなときはxargsを使うと、パイプの出力を引数に変換して渡せます。

#!/bin/bash

# .tmpファイルを検索し、その一覧をrmの引数として渡して削除する
find . -name "*.tmp" | xargs rm

findの出力(標準出力)がそのままrmの引数(コマンドライン引数)に変換される点が、通常のパイプとの違いです。

まとめ

この記事のポイント

  • パイプ(|)はコマンドの出力を次のコマンドの入力に流す、ベルトコンベアのような仕組み
  • リダイレクト(>>><)は出力先・入力元をファイルに差し替える仕組み
  • >は上書き、>>は追記。ログ運用では取り違えるとデータを消失するので要注意
  • teeは画面表示とファイル保存の両立、xargsはパイプの出力を引数に変換するのに使う

次に読むべき記事

コマンドの組み合わせ方がわかったら、次は処理を部品化する「シェル関数の定義と使い方」を読んでみてください。

タグ: Shell, 中級者向け, 引数・入出力

タイトルとURLをコピーしました