シェルスクリプトのshoptコマンドでbash動作を設計する方法|globstar・nullglob・nocasematchの実践活用

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)シェルスクリプト > シェルスクリプトのshoptコマンドでbash動作を設計する方法|globstar・nullglob・nocasematchの実践活用
「forループとglobを組み合わせたら、マッチするファイルが0件の時にパターン文字列がそのまま引数に渡ってしまった」
「サブディレクトリも含めてファイルを一括処理したいのに、findとループの組み合わせが長くなりすぎる」
「case文でユーザー入力の大文字小文字の揺れに毎回対応しなければならない」

こうした問題は、bashの動作フラグを一行変えるだけで解決できます。shopt(shell options)コマンドを使うと、globのパターン展開・ゼロヒット時の挙動・文字列比較のルールをスクリプト単位で切り替えられます。コマンドを1つ覚えるだけで、堅牢さが大きく変わります。

この記事では、シェルスクリプトの設計に直結するshoptオプションを解説します。globstar(**でサブディレクトリを再帰)・nullglob(ゼロヒット時に空配列)・nocasematch(大文字小文字を吸収)を中心に、Rocky Linux 9.4 / Ubuntu 24.04 LTSで動作確認した実例で紹介します。

この記事のポイント

・shopt -s で有効化、shopt -u で無効化。設定はスクリプト内でのみ有効
・globstar を有効にすると ** でサブディレクトリを再帰処理でき、findの代替になる
・nullglob を有効にするとマッチなし時に空配列になり、globループの誤動作を防げる
・nocasematch を有効にするとcase文・[[ ]]が大文字小文字を区別しなくなる


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

shoptコマンドとはなにか|bashの動作スイッチ

bashには set コマンドや環境変数では設定できない「bash固有の動作フラグ」が40種類以上用意されています。その設定窓口が shopt です。

たとえば、デフォルトのbashでは *.txt のglobにマッチするファイルが0件だった場合、エラーも空配列も返さず、リテラル文字列 *.txt をそのまま引数として渡します。この挙動が原因で「ファイルが存在しないのに処理が走った」というバグが生まれます。shopt -s nullglob 一行で、ゼロヒット時は空配列を返すよう変更できます。

shoptの設定はスクリプト内でのみ有効です。スクリプトが終了すれば元の状態に戻るため、システム全体の設定を汚しません。

基本的な使い方|確認・有効化・無効化

1. 現在の設定を確認する

引数なしで実行すると、全オプションとその状態が一覧表示されます。

# 全オプションの一覧 shopt # 特定オプションの確認 shopt globstar shopt nullglob

実行例(Rocky Linux 9.4):

$ shopt globstar globstar off $ shopt nullglob nullglob off $ shopt nocasematch nocasematch off

デフォルトでは主要なオプションはすべてoffです。スクリプトの先頭で明示的に有効化する設計にしてください。

2. 有効化(-s)と無効化(-u)

# オプションを有効化 shopt -s globstar # オプションを無効化 shopt -u globstar # 有効化後に確認 shopt globstar

$ shopt -s globstar $ shopt globstar globstar on

3. スクリプトの先頭でまとめて設定する

必要なオプションはスクリプトの冒頭にまとめて書くのが設計の基本です。後から有効化すると、それ以前のglobが意図と異なる動きをするリスクがあります。

#!/bin/bash set -euo pipefail # shoptの設定はファイルの先頭でまとめて宣言する shopt -s globstar # **でサブディレクトリ再帰 shopt -s nullglob # マッチなし時に空配列 shopt -s nocasematch # 大文字小文字を無視

globstar|**でサブディレクトリを再帰処理する

1. globstarを有効化してファイルを再帰処理する

デフォルトでは *** と同じ意味を持ちます。shopt -s globstar を設定すると、** が「このディレクトリ以下のすべてのパス」にマッチするようになります。

#!/bin/bash shopt -s globstar shopt -s nullglob # /var/log 以下の .log ファイルをすべて処理する for f in /var/log/**/*.log; do echo "Processing: $f" done

実機での出力例(Rocky Linux 9.4):

Processing: /var/log/audit/audit.log Processing: /var/log/dnf.log Processing: /var/log/dnf.librepo.log Processing: /var/log/dnf.rpm.log Processing: /var/log/httpd/access_log Processing: /var/log/httpd/error_log

2. findとの使い分け設計

globstarとfindは用途で使い分けます。

globstarが適するケース:シンプルな拡張子フィルタ・少ないディレクトリ数・for文で配列として扱いたい場合
findが適するケース:-mtime(更新日時)-size(サイズ)-perm(権限)など追加条件が必要な場合

条件がシンプルなバッチ処理は globstar の方がコードがすっきりします。複雑な絞り込みが必要な場合は find を選んでください。

3. ディレクトリ自体は除外する

** はファイルだけでなくディレクトリ自体にもマッチします。ファイルのみを処理したい場合は条件分岐で除外します。

#!/bin/bash shopt -s globstar nullglob for f in /backup/**/*; do # ディレクトリはスキップ [[ -d "$f" ]] && continue echo "File: $f" done

nullglobとfailglob|ゼロヒット時の安全設計

1. デフォルトの危険な振る舞いを理解する

nullglobが無効(デフォルト)の場合、globにマッチするファイルが0件だとリテラル文字列がそのまま返ります。これが予期しないバグの温床です。

# nullglob 無効(デフォルト) # /tmp/work/ にファイルが1件もない場合 for f in /tmp/work/*.csv; do echo "処理: $f" done

処理: /tmp/work/*.csv

ファイルが0件なのに「処理」の行が表示され、後続のコマンドに /tmp/work/*.csv という文字列が渡ります。削除・移動・変換などの処理を実行するスクリプトでは、これが深刻な誤動作につながります。

2. nullglobで安全に処理する

#!/bin/bash shopt -s nullglob files=(/tmp/work/*.csv) echo "対象ファイル数: ${#files[@]}" if [[ ${#files[@]} -eq 0 ]]; then echo "処理対象なし。終了します。" exit 0 fi for f in "${files[@]}"; do echo "処理: $f" done

対象ファイル数: 0 処理対象なし。終了します。

nullglobを有効にすると、globにマッチするファイルが0件の場合に配列が空(${#files[@]} == 0)になります。事前に件数チェックを入れることで、ゼロ件時の処理を明示的に制御できます。

3. failglobでゼロヒットをエラーにする

「対象ファイルが必ず存在しているはずの処理」では、ゼロ件をエラーとして扱う方が安全です。

#!/bin/bash shopt -s failglob # マッチなし時はbashがエラーを出す for f in /required/dir/*.conf; do echo "読み込み: $f" done

bash: no match: /required/dir/*.conf

nullglobとfailglobは同時に有効化しないでください。両方を設定するとfailglobが優先されます。スクリプトの要件に応じて一方だけを選んでください。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
シェルスクリプト講座を見る >>

nocasematch|大文字小文字を区別しないマッチング設計

1. case文での活用

ユーザーの入力や環境変数の値は、大文字小文字の揺れが発生しがちです。nocasematch を有効にすると、case文や [[ ]] での比較が大文字小文字を区別しなくなります。

#!/bin/bash shopt -s nocasematch env_type="${1:-production}" case "$env_type" in production) echo "本番環境への適用" ;; staging) echo "ステージング環境への適用" ;; dev*) echo "開発環境への適用" ;; *) echo "不明な環境: $env_type" exit 1 ;; esac

$ bash deploy.sh STAGING ステージング環境への適用 $ bash deploy.sh Staging ステージング環境への適用 $ bash deploy.sh staging ステージング環境への適用

「STAGING」「Staging」「staging」どれでも同じcaseブランチにマッチします。大文字小文字ごとにpatternを列挙する必要がなくなります。

2. [[ ]] の正規表現マッチングへの影響

nocasematch[[ ]] 内の == パターンマッチと =~ 正規表現マッチにも適用されます。

#!/bin/bash shopt -s nocasematch os_info=$(cat /etc/os-release 2>/dev/null | grep '^ID=' | cut -d= -f2 | tr -d '"') if [[ "$os_info" =~ rhel|rocky|almalinux|centos ]]; then echo "Red Hat系: dnf/yum を使用" elif [[ "$os_info" =~ ubuntu|debian ]]; then echo "Debian系: apt を使用" else echo "不明なOS: $os_info" fi

OSのIDが大文字で返ってくるケース(一部のコンテナイメージ等)でも正しくマッチします。

その他の実用的なshoptオプション

1. extglob|拡張glob構文で複雑なパターンを記述する

extglob を有効にすると、@(...)(いずれか1つ)・+(...)(1回以上)・!(...)(否定)などの拡張glob構文が使えます。

#!/bin/bash shopt -s extglob nullglob # .log と .err 以外のファイルを処理する for f in /var/log/app/!(*.log|*.err); do echo "対象: $f" done # .sh または .bash で終わるファイルだけを処理 for f in /usr/local/bin/@(*.sh|*.bash); do echo "スクリプト: $f" done

2. dotglob|ドットファイルをglobに含める

デフォルトでは * のglobは .bashrc などのドットファイルにマッチしません。dotglob を有効にすると対象に含まれます。

#!/bin/bash shopt -s dotglob nullglob # ホームディレクトリのドットファイルも含めてバックアップ for f in /home/operator/*; do cp "$f" /backup/home/ done

3. cdspell|cd時のスペルミスを自動補正する

対話的なシェルで役立つオプションです。cdspell を有効にすると、cd 時のディレクトリ名のスペルミスを自動修正します。スクリプトより ~/.bashrc での設定が主な用途です。

# ~/.bashrc に追記 shopt -s cdspell

トラブルシュート|よくある問題と対処

1. macOS(bash 3.2)でglobstarが使えない

macOSに標準でインストールされているbashはバージョン3.2であり、globstar は bash 4.0以降で追加されたオプションのため使えません。macOS環境でも使いたい場合は Homebrew でbash 5.x をインストールするか、findコマンドで代替してください。

# バージョン確認 bash --version

$ bash --version GNU bash, version 5.2.26(1)-release (x86_64-redhat-linux-gnu)

2. サブシェルではshoptの設定が引き継がれない場合がある

(...) で作ったサブシェルは親のshopt設定を引き継ぎますが、bash -c で起動した場合や別スクリプトを bash script.sh で呼び出した場合は引き継がれません。呼び出し先のスクリプトに必要なshoptを再度記述するか、source で読み込んでください。

3. nullglobとfailglobの競合

nullglobとfailglobを同時に shopt -s するとfailglobが優先されます。どちらか一方だけを設定してください。設定を間違えたと気づいたら shopt -u failglob で無効化できます。

本記事のまとめ

オプション 有効化コマンド 効果・主な用途
globstar shopt -s globstar ** でサブディレクトリを再帰処理。findの簡易代替
nullglob shopt -s nullglob マッチなし時に空配列を返す。globループの誤動作防止
failglob shopt -s failglob マッチなし時にエラー終了。必須ファイルの存在保証
nocasematch shopt -s nocasematch case文・[[ ]] で大文字小文字を区別しない
extglob shopt -s extglob !(...) @(...) 等の拡張glob構文を有効化
dotglob shopt -s dotglob ドットファイルを * globに含める
cdspell shopt -s cdspell cd時のスペルミスを自動補正(対話シェル向け)
shoptを使いこなすと「書かなくてよいコード」が増えます。特にnullglobは全てのbashスクリプトのデフォルト設定に加えることを強くお勧めします。globが0件でもリテラル文字列を処理しようとするバグは、本番環境でひっそりと誤動作を起こすことが多く、nullglobを入れておくだけでそのリスクを根本から排除できます。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
シェルスクリプト講座を見る >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。