BashのGlob パターンマッチ使用時に該当ファイルが見つからなかった場合の挙動を理解する

2023-03-27Bash

はじめに

Bash で該当ディレクトリ内にあるファイルを一括でコマンドの引数として渡したい場合は、 * を使った Glob 機能が便利です。

さて、この * を使ったワイルドカードによるコマンドライン引数の展開ですが、「パターンにマッチするファイルが無かった場合」に想定しない挙動をしてしまうことがあります。

「パターンにマッチするファイルが無かった場合」にどんな問題が起きるかその対処法 を紹介します。

検証環境

$ uname -moi
aarch64 aarch64 GNU/Linux

$ head -n 2 /etc/os-release
NAME="Ubuntu"
VERSION="21.04 (Hirsute Hippo)"

$ bash -version | head -n 1
GNU bash, バージョン 5.1.4(1)-release (aarch64-unknown-linux-gnu)

作業の流れ(作業ログ)

Glob によるパラーんマッチの挙動を確認するために、
事前にカレントディレクトリに数ファイル作成しておきます。

名前は単純に以下のような数字の連番としておきます。

  • 10.txt
  • 11.txt
  • 12.txt
$ touch 10.txt 11.txt 12.txt

Glob によるパターンマッチを試してみる

それでは Bash Glob によるパターンマッチの機能を使ってみます。

Glob 機能ではパターンにマッチした部分がコマンドライン引数として展開されます。 ( 厳密には引数以外の部分にも適用されますが、引数として利用するケースがほとんどのはずです。 )

パターンが 1 ファイル以上にマッチした場合

まずはファイル名が .txt で終わるファイルを検出し、コマンドライン引数として展開された場合の挙動をみてみましょう。

これは echo 10.txt 11.txt 12.txt を実行した結果と同様になります。

# .txt で終わるファイルを検出し、コマンドライン引数として渡す
$ echo *.txt
10.txt 11.txt 12.txt

次に、ファイル名が 1.txt で終わるファイルを検出し、コマンドライン引数として展開された場合の挙動をみてみましょう。

これは echo 11.txt を実行した結果と同様になります。

$ echo *1.txt
11.txt

ここまではなんの問題もありませんね。

パターンがどのファイルにもマッチしなかった場合

今度はパターンの結果がどのファイルにもマッチしなかった場合の挙動を見ていきます。

ファイル名が 3.txt で終わるファイルを検出し、コマンドライン引数として展開してみます。

$ echo *3.txt
*3.txt

ファイルが検出されたなかった場合ですが、展開前の文字列がそのままコマンドライン引数として渡ってしまいます。

これを意図した挙動として取り扱う場合は少ないと思います。

set -o nullglob オプションを使用する

*3.txt のようなどのファイルにもマッチしないようなパターンが指定されたときには何も表示されないようにしたい場合は、
set -o nullglob という Bash の設定を行います。

# パターンマッチなしの場合のコマンドライン引数展開の挙動を変更する
$ shopt -s nullglob

# 3ファイル見つかるケース
$ echo *.txt
10.txt 11.txt 12.txt

# 1ファイル見つかるケース
$ echo *1.txt
11.txt

# ファイルが見つからないケース
$ echo *3.txt

このように、

  • パターンマッチしたファイルが見つかった場合の挙動はかわりなし
  • パターンマッチしたファイルがない場合には引数が空となる

ことがわかります。

ひとこと

というような問題があったり、 フォルダがサブディレクトリに格納されているケースも考えないといけないことがあったりします。

ですので僕は find をファイル名にスペースを含んでいても正しく処理できるようなオプション付きで実行することがほとんどです。

やり方は過去のエントリで取り上げています。

2023-03-27Bash