シェルスクリプト実践テクニック集|ワンライナー・パイプ・条件分岐の応用

kento_morota 12分で読めます

シェルスクリプトは、サーバー管理やCI/CDパイプラインの構築、日常的なタスク自動化に欠かせないスキルです。基本的なコマンドは知っていても、実務で使える応用テクニックを体系的に学ぶ機会は意外と少ないのではないでしょうか。

本記事では、ワンライナーの組み立て方、パイプラインの活用、条件分岐とループの応用、エラーハンドリング、デバッグ手法まで、現場で即使えるシェルスクリプトのテクニックを実践的に解説します。

シェルスクリプトの基盤:シバンとベストプラクティス

実践的なシェルスクリプトを書く前に、堅牢なスクリプトのための基盤を固めましょう。

シバン(Shebang)の選択

スクリプトの1行目に記述する#!/bin/bashはシバンと呼ばれ、スクリプトを実行するインタープリターを指定します。環境の移植性を高めたい場合は#!/usr/bin/env bashと書くことで、PATHからbashを検索して実行します。

POSIX互換のスクリプトを書きたい場合は#!/bin/shを使いますが、Bash固有の機能(配列、[[演算子など)は使えなくなります。

堅牢なスクリプトのためのsetオプション

スクリプトの冒頭に以下のsetオプションを記述することで、多くのバグを未然に防げます。

set -e(errexit)
コマンドがエラー(非ゼロの終了コード)を返した時点でスクリプトを終了します。エラーを無視して処理が続行されるのを防ぎます。

set -u(nounset)
未定義の変数を参照した場合にエラーにします。変数名のタイプミスによるバグを防止できます。

set -o pipefail
パイプライン中のいずれかのコマンドがエラーを返した場合、パイプライン全体のエラーとして扱います。デフォルトではパイプラインの最後のコマンドの終了コードのみが使われるため、途中のエラーが見逃されることがあります。

これら3つをまとめてset -euo pipefailと記述するのが定番です。

変数のクォーティング

変数は常にダブルクォートで囲みましょう。"$variable"とすることで、変数の値にスペースやグロブ文字が含まれていても安全に展開されます。クォートしないと、ファイル名にスペースが含まれる場合などに意図しない動作になります。

パイプラインの活用テクニック

パイプ(|)はUnix哲学の根幹を成す機能で、コマンドの出力を別のコマンドの入力に接続します。パイプラインを使いこなすことで、複雑なデータ処理をワンライナーで実現できます。

テキスト処理の基本パイプライン

ログからエラー行を抽出して集計する
grep "ERROR" app.log | awk '{print $4}' | sort | uniq -c | sort -rn | head -10
grepでERROR行を抽出し、awkで特定のフィールド(たとえばエラーの種類)を取り出し、sort + uniq -cで重複をカウントし、出現回数の降順で上位10件を表示します。

CSVの特定列を加工する
cut -d',' -f2,5 data.csv | sed '1d' | sort -t',' -k2 -rn
cutで2列目と5列目を抽出し、sedでヘッダ行を削除し、5列目の数値で降順ソートします。

プロセス置換の活用

Bashのプロセス置換<(command)を使うと、コマンドの出力をファイルとして扱えます。2つのファイルの差分を取る際に、前処理を挟みたい場合に便利です。

diff <(sort file1.txt) <(sort file2.txt)とすれば、2つのファイルをソートしてから差分を取れます。一時ファイルを作る必要がありません。

xargsによる並列実行

xargsは、標準入力から受け取った値を引数としてコマンドを実行するツールです。-Pオプションで並列実行できます。

find . -name "*.jpg" -print0 | xargs -0 -P 4 -I {} convert {} -resize 50% resized/{}
JPEGファイルを見つけて、4並列でリサイズ処理を実行する例です。-print0-0の組み合わせで、ファイル名にスペースが含まれていても安全に処理できます。

条件分岐の応用テクニック

シェルスクリプトの条件分岐は、ファイルの存在確認や文字列比較だけではありません。応用的な使い方を見てみましょう。

テスト演算子の使い分け

Bashでは[ ](testコマンド)と[[ ]](Bash拡張)の2つの条件式が使えます。[[ ]]の方が安全で柔軟です。

[[ ]]の利点

変数のクォートを忘れてもワード分割されません。&&||で条件を結合できます。=~で正規表現マッチングが使えます。<>で文字列の辞書順比較ができます。

正規表現による条件分岐
[[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]とすれば、メールアドレスの形式チェックができます。

デフォルト値とパラメータ展開

Bashのパラメータ展開を使って、変数のデフォルト値設定や値の検証をシンプルに書けます。

${var:-default}
varが未定義または空の場合にdefaultを返します。環境変数のデフォルト値設定に便利です。PORT="${PORT:-8080}"とすれば、PORT未指定時は8080がデフォルトになります。

${var:?error_message}
varが未定義または空の場合にエラーメッセージを表示してスクリプトを終了します。必須の環境変数のチェックに使えます。: "${DATABASE_URL:?DATABASE_URL must be set}"のように使います。

${var%pattern}と${var#pattern}
文字列の末尾(%)や先頭(#)からパターンを除去します。${filename%.txt}で拡張子を除去、${path##*/}でベースネームを取得できます。

case文による分岐

複数のパターンに対する分岐には、if-elif-elseの連鎖よりもcase文の方が可読性が高いです。グロブパターンが使えるため、柔軟なマッチングが可能です。

case文は引数のパース、ファイル種別の判定、OSの検出などに特に便利です。case "$OSTYPE" in linux*) ... ;; darwin*) ... ;; esacでOS別の処理を分けられます。

ループと配列の応用

ループと配列を効果的に使うことで、複雑なデータ処理を実現できます。

安全なファイルイテレーション

ファイルの一覧を処理する際、for file in $(ls)は危険です。ファイル名にスペースや特殊文字が含まれると正しく動作しません。代わりにグロブパターンを使いましょう。

for file in *.txt; do ... doneとすれば安全にファイルをイテレートできます。サブディレクトリも含めたい場合はshopt -s globstarを有効にして**/*.txtを使います。

findコマンドの結果を安全にイテレートするには、find . -name "*.txt" -print0 | while IFS= read -r -d '' file; do ... doneのようにNULL区切りで読み取ります。

配列の活用

Bashの配列は、複数の値を管理する際に便利です。

配列の宣言と操作
arr=("apple" "banana" "cherry")で宣言し、${arr[0]}で要素にアクセスします。${arr[@]}で全要素を展開、${#arr[@]}で要素数を取得します。

連想配列(Bash 4以降)
declare -A mapで連想配列を宣言し、map[key]="value"で値を設定します。設定ファイルの読み込みや、キーと値のペアの管理に活用できます。

whileループでのデータ読み取り

whileループとreadコマンドの組み合わせで、ファイルやコマンド出力を行ごとに処理できます。

while IFS=',' read -r col1 col2 col3; do ... done < data.csv
CSVファイルを行ごとに読み取り、フィールドを変数に分割する例です。IFS(Internal Field Separator)でデリミタを指定します。

関数とエラーハンドリング

スクリプトが複雑になるほど、関数による構造化とエラーハンドリングが重要になります。

関数の定義と活用

Bashの関数はfunction_name() { ... }で定義します。引数は$1$2で受け取り、returnで終了コード(0〜255)を返します。文字列を返したい場合は標準出力に出力し、呼び出し側で$(function_name)でキャプチャします。

ローカル変数
関数内でlocalキーワードを使って変数を宣言しましょう。localを使わないとグローバル変数になり、関数外のスクリプトに影響を与えるバグの原因になります。

trapによるクリーンアップ処理

trapコマンドは、シグナルを受け取った際やスクリプト終了時に実行する処理を登録します。一時ファイルの削除やロックの解除などのクリーンアップに使います。

trap 'rm -f "$tmpfile"' EXITとすれば、スクリプトが正常終了しても異常終了しても一時ファイルが削除されます。EXITの代わりにINT(Ctrl+C)、TERM(killコマンド)、ERR(エラー時)を指定することもできます。

エラー処理のパターン

即時終了パターン
set -eとの組み合わせで、エラー発生時に即座にスクリプトを終了します。trapでクリーンアップ処理を登録しておけば、異常終了時の後処理も安全に行えます。

リトライパターン
ネットワーク通信など、一時的な失敗が起こりうる処理にはリトライ機構を組み込みましょう。ループとsleepを組み合わせて、指数バックオフ(リトライ間隔を徐々に増やす)を実装するのが一般的です。

ログ出力関数
エラーメッセージは標準エラー出力(stderr)に出力しましょう。log_error() { echo "[ERROR] $*" >&2; }のようなユーティリティ関数を用意しておくと便利です。タイムスタンプの付加も有用です。

実践ワンライナー集

日常的に使える実践的なワンライナーを紹介します。

ファイル操作系

指定サイズ以上のファイルを検索
find /var/log -type f -size +100M -exec ls -lh {} + 2>/dev/null
100MB以上のファイルを検索し、詳細情報を表示します。

特定の拡張子を一括変換
for f in *.jpeg; do mv -- "$f" "${f%.jpeg}.jpg"; done
.jpegを.jpgに一括リネームします。

空のディレクトリを削除
find . -type d -empty -delete
空のディレクトリを再帰的に削除します。

テキスト処理系

JSONからキーを抽出(jq使用)
curl -s https://api.example.com/data | jq -r '.items[] | [.id, .name] | @tsv'
APIレスポンスからidとnameフィールドをTSV形式で抽出します。

重複行の検出
sort file.txt | uniq -d
ファイル内の重複行だけを表示します。

特定パターンの行間を抽出
sed -n '/BEGIN/,/END/p' file.txt
BEGINとENDで囲まれたブロックを抽出します。

システム管理系

ポートを使用しているプロセスの確認
ss -tlnp | awk 'NR>1 {print $4, $6}' | sort
リッスン中のTCPポートとプロセス情報を表示します。

ディスク使用量の上位ディレクトリ
du -h --max-depth=1 /home | sort -rh | head -10
/home配下のディレクトリのディスク使用量を降順で表示します。

デバッグとパフォーマンス

シェルスクリプトのデバッグ手法とパフォーマンス改善のテクニックを紹介します。

デバッグテクニック

set -x(xtrace)
実行されるコマンドを実行前に表示します。スクリプト全体に適用する場合は冒頭に記述し、特定の箇所だけデバッグしたい場合はset -xset +xで囲みます。

bashdb
Bash用のデバッガーです。ブレークポイントの設定、ステップ実行、変数の確認などが可能です。

ShellCheck
シェルスクリプトの静的解析ツールです。一般的なバグパターン、未クォートの変数、非推奨の構文などを検出します。CIパイプラインに組み込んで、コード品質を自動チェックしましょう。

パフォーマンスの改善

サブシェルの回避
$(command)はサブシェルを起動するため、ループ内での多用は避けましょう。変数の操作にはパラメータ展開(${var%pattern}など)を使う方が高速です。

外部コマンドの呼び出し削減
ループ内でgrepやsedを繰り返し呼び出す代わりに、awkの1回の実行で処理を完結させると大幅に高速化できます。

GNU Parallelの活用
xargsの-Pオプションよりも柔軟な並列実行が可能です。大量のファイル処理や、独立した複数のタスクの並列実行に効果的です。

まとめ

シェルスクリプトは、set -euo pipefailによる堅牢な基盤の上に、パイプラインによるデータ処理、パラメータ展開による柔軟な文字列操作、trapによるクリーンアップ処理を組み合わせることで、信頼性の高い自動化スクリプトを構築できます。

ワンライナーの組み立てスキルは日常の作業効率を大幅に向上させ、ShellCheckによる静的解析はバグの早期発見に貢献します。まずは日常的に手作業で行っている操作をワンライナーで自動化するところから始め、徐々にスクリプトとして構造化していくのが実践的な学習方法です。

#シェルスクリプト#Bash#Linux
共有:
無料メルマガ

週1回、最新の技術記事をお届け

AI・クラウド・開発の最新記事を毎週月曜にメールでお届けします。登録は無料、いつでも解除できます。

プライバシーポリシーに基づき管理します

起業準備に役立つ情報、もっとありますよ。

まずは話だけ聞いてもらう