【図解:bash】 パス名展開(ワイルドカード)の使用方法

複数の文字列を作る ブレース展開 は、別の記事で紹介しました。この記事では、複数のファイル名やディレクトリ名をパターンで指定できる、パス名展開(ワイルドカード)の使い方を、実行例と図で説明します。

ワイルドカードは、任意の文字列などを表す特殊文字です。コマンドを実行する前に、bash がワイルドカードを含むパターンを、一致するファイル名やディレクトリ名に置き換えます。

この記事でわかること
  1. *(任意の文字列)と ?(任意の1文字)で名前のパターンを書けること。パターンは、コマンドではなくシェルが展開すること
  2. [ ] で文字の候補や範囲を指定でき、! で除外、[[:upper:]] などの文字クラスで文字の種類を指定できること
  3. { } はパス名展開ではなくブレース展開で、パス名展開より先に処理されること

パス名展開とワイルドカード

シェルは、コマンドラインの中にワイルドカードを含む単語があると、その単語を、一致する名前に置き換えてからコマンドを実行します。たとえば echo d* では、echo が d* を受け取るのではなく、シェルが置き換えた data1 data11 … が echo に渡されます。置き換わった名前は、ロケールの照合順に並んだ複数の引数になります。

$ echo d*① シェルが d* に合う名前を探して置き換える(echo の実行前)$ echo data1 data11 data111 dir1 dir11② echo が受け取った引数をそのまま表示するdata1 data11 data111 dir1 dir11
図1 echo d* は、シェルが名前に置き換えてから echo に渡される

パスを含まない記述(d* など)は、カレントディレクトリの名前が対象です。使えるワイルドカードは次のとおりです。

記号意味例
*任意の文字列(空の文字列も含む)d*、*11
?任意の1文字???11
[ABC]角括弧の中の、どれか1文字[ABC]*
[a-f]範囲の中の、どれか1文字[a-f]*
[!1-3]範囲に含まれない1文字([^1-3] も同じ)*[!1-3][1-3][1-3]
[[:upper:]]文字クラスに含まれる1文字[[:upper:]]*

この記事では、次のファイルとディレクトリがあるカレントディレクトリで試します。drwx… で始まる dir1 と dir11 がディレクトリで、ほかはサイズ 0 のファイルです。

hirotano@linux-1:~$ ls -l
合計 8
-rw-rw-r-- 1 hirotano hirotano    0 12月 24 06:17 1-music
-rw-rw-r-- 1 hirotano hirotano    0 12月 24 06:17 11-music
-rw-rw-r-- 1 hirotano hirotano    0 12月 24 06:17 2-music
-rw-rw-r-- 1 hirotano hirotano    0 12月 24 06:05 Backup111
-rw-rw-r-- 1 hirotano hirotano    0 12月 24 06:05 Backup222
-rw-rw-r-- 1 hirotano hirotano    0 12月 24 06:05 Backup333
-rw-rw-r-- 1 hirotano hirotano    0 12月 24 06:06 IMAGE
-rw-rw-r-- 1 hirotano hirotano    0 12月 24 06:05 IMAGE1
-rw-rw-r-- 1 hirotano hirotano    0 12月 24 06:06 IMAGE12
-rw-rw-r-- 1 hirotano hirotano    0 12月 24 06:04 data1
-rw-rw-r-- 1 hirotano hirotano    0 12月 24 06:04 data11
-rw-rw-r-- 1 hirotano hirotano    0 12月 24 06:04 data111
drwxrwxr-x 2 hirotano hirotano 4096 12月 24 06:03 dir1
drwxrwxr-x 2 hirotano hirotano 4096 12月 24 06:03 dir11
-rw-rw-r-- 1 hirotano hirotano    0 12月 24 06:04 file1
-rw-rw-r-- 1 hirotano hirotano    0 12月 24 06:04 file2.txt
補足:展開結果は echo で確かめる

パターンがどの名前に展開されるかは、echo で確認できます。rm や mv などに使う前に、echo で展開結果を確かめておくと、意図しない名前を巻き込む失敗を防げます。

任意の文字列(*)

* は、任意の文字列に一致します。空の文字列にも一致します。

echo*任意の文字列(空も含む)一致した名前(出力の順)1–music11–music2–musicBackup111Backup222Backup333IMAGEIMAGE1IMAGE12data1data11data111dir1dir11file1file2.txt
図2 echo * は、カレントディレクトリのすべての名前に展開される
hirotano@linux-1:~$ echo *
1-music 11-music 2-music Backup111 Backup222 Backup333 IMAGE IMAGE1 IMAGE12 data1 data11 data111 dir1 dir11 file1 file2.txt

カレントディレクトリにある16個の名前が、すべて展開されました。

先頭文字を指定した展開

d* は、先頭が d で、そのあとが任意の文字列の名前に一致します。

echod*先頭は d任意の文字列一致した名前(出力の順)data1data11data111dir1dir11
図3 d* は d で始まる名前に一致する
hirotano@linux-1:~$ echo d*
data1 data11 data111 dir1 dir11

末尾の文字列を指定した展開

*11 は、末尾が 11 の名前に一致します。Backup111 は、末尾の2文字が 11 なので一致します。

echo*11任意の文字列末尾は 11一致した名前(出力の順)Backup111data11data111dir11
図4 *11 は 11 で終わる名前に一致する
hirotano@linux-1:~$ echo *11
Backup111 data11 data111 dir11

開始、および終了の文字列を指定して展開

d*11 は、先頭が d で、末尾が 11 の名前に一致します。

echod*11先頭は d任意の文字列末尾は 11一致した名前(出力の順)data11data111dir11
図5 d*11 は d で始まり 11 で終わる名前に一致する
hirotano@linux-1:~$ echo d*11
data11 data111 dir11
補足:* が一致しないもの

* は空の文字列にも一致するので、d*11 は、たとえば d11 という名前にも一致します。

また、* や ? は、先頭がピリオド(.)の名前(隠しファイル)には一致しません。/(パスの区切り)にも一致しないので、サブディレクトリの中の名前は、dir*/* のようにパスを区切って書きます。

任意の1文字(?)

? は、任意の1文字に一致します。文字数が決まっている名前を指定するときに使います。????? は、? が5つなので、5文字の名前に一致します。

echo?????任意の1文字 × 5(名前は5文字)一致した名前(出力の順)IMAGEdata1dir11file1
図6 ????? は5文字の名前に一致する
hirotano@linux-1:~$ echo ?????
IMAGE data1 dir11 file1

先頭の文字数を指定した展開

???11 は、任意の3文字のあとに 11 が続く、5文字の名前に一致します。

echo???11任意の1文字 × 3末尾は 11一致した名前(出力の順)dir11
図7 ???11 は任意の3文字のあとに 11 が続く名前に一致する
hirotano@linux-1:~$ echo ???11
dir11

末尾の文字数を指定した展開

f*.??? は、先頭が f で、ピリオドのあとが任意の3文字(拡張子が3文字)の名前に一致します。* と ? は、1つのパターンの中で組み合わせられます。

echof*.???先頭は f任意の文字列ピリオド任意の1文字 × 3一致した名前(出力の順)file2.txt
図8 f*.??? は f で始まり、ピリオドのあとが3文字の名前に一致する
hirotano@linux-1:~$ echo f*.???
file2.txt

文字指定([ ])

角括弧 [ ] の中に書いた文字のうち、どれか1文字に一致します。1文字を指定するので、複数の文字に一致させたいときは * と組み合わせます。

指定文字のいずれかで展開

[ABC]* は、先頭が A、B、C のどれかで、そのあとが任意の文字列の名前に一致します。

echo[ABC]*A か B か C のどれか1文字任意の文字列一致した名前(出力の順)Backup111Backup222Backup333
図9 [ABC]* は A、B、C のどれかで始まる名前に一致する
hirotano@linux-1:~$ echo [ABC]*
Backup111 Backup222 Backup333

指定した文字範囲で展開

ハイフン(-)を使うと、文字の範囲を指定できます。[a-f]* は、先頭が a から f のどれかの名前に一致します。

echo[a–f]*a から f の範囲の1文字任意の文字列一致した名前(出力の順)data1data11data111dir1dir11file1file2.txt
図10 [a-f]* は a から f のどれかで始まる名前に一致する
hirotano@linux-1:~$ echo [a-f]*
data1 data11 data111 dir1 dir11 file1 file2.txt
補足:範囲指定とロケール

a-f のような範囲の並びは、bash の設定によって、ロケールの照合順で解釈される場合があります。その場合、[a-f] に大文字(B など)が含まれることがあります。shopt globasciiranges で現在の設定を確認でき、on のときは、ロケールに関係なく ASCII の並びで解釈されます。大文字か小文字かを確実に指定したいときは、あとで説明する [[:upper:]]、[[:lower:]] を使います。

指定した数字範囲で展開

数字も範囲で指定できます。*[1-3][1-3][1-3] は、末尾の3文字が、すべて 1 から 3 のどれかの名前に一致します。data11 は、末尾の3文字が a11 なので一致しません。

echo*[1–3][1–3][1–3]任意の文字列1 から 3 のどれか1文字 × 3一致した名前(出力の順)Backup111Backup222Backup333data111
図11 *[1-3][1-3][1-3] は末尾が1から3の数字3つの名前に一致する
hirotano@linux-1:~$ echo *[1-3][1-3][1-3] 
Backup111 Backup222 Backup333 data111

指定した文字を含めず展開

角括弧の先頭に ! を付けると、その範囲に含まれない1文字に一致します。*[!1-3][1-3][1-3] は、末尾の3文字が、「1 から 3 以外の1文字」と「1 から 3 のどれか2文字」の順で並ぶ名前に一致します。

echo*[!1–3][1–3][1–3]任意の文字列1 から 3 以外の1文字1 から 3 のどれか × 2一致した名前(出力の順)IMAGE12data11dir11
図12 *[!1-3][1-3][1-3] の一致するようす
hirotano@linux-1:~$ echo *[!1-3][1-3][1-3]
IMAGE12 data11 dir11

IMAGE12 は E、data11 は a、dir11 は r が、1から3以外の文字として一致しました。Backup111 は、末尾の3文字がすべて 1 なので、一致しません。! の代わりに ^ でも同じ指定ができます。

文字列指定({ })

中括弧 { } は、ここまでのワイルドカードとは別の、ブレース展開です。コンマで区切った文字列を、順番に展開します。{file1,data*} は、file1 と data* に展開されます。

$ echo {file1,data*}① ブレース展開(ファイルがあるかどうかは見ない)$ echo file1 data*② パス名展開(data* に合う名前へ置き換える)$ echo file1 data1 data11 data111
図13 echo {file1,data*} は、ブレース展開のあとにパス名展開が行われる
hirotano@linux-1:~$ echo {file1,data*}
file1 data1 data11 data111

ブレース展開が先に処理され、そのあとで、data* がパス名展開で data1 data11 data111 に置き換わります。ブレース展開は、ファイルがあるかどうかを調べずに展開するので、存在しない名前でも展開されます。ブレース展開の詳しい使い方は、【図解:bash】ブレース展開の使用方法 にまとめています。

文字クラス定義([[: :]])

文字の種類は、[[:クラス名:]] の形で指定できます。[[:upper:]] は大文字の1文字、[[:lower:]] は小文字の1文字、[[:digit:]] は数字の1文字です。

echo[[:class:]][[: と :]] で囲んでクラス名を書くecho[[:upper:]]upper = 大文字1文字(A から Z)
図14 文字クラスは [[:クラス名:]] の形で書く
クラス名意味
alpha英字
digit数字(0 から 9)
alnum英数字
upper大文字
lower小文字
space空白類(スペース、タブ、改行など)
punct記号(英数字と空白以外の、印字できる文字)
xdigit16進数に使う文字(0〜9、a〜f、A〜F)

このほかに、blank、cntrl、graph、print、ascii、word があります。

補足:二重の角括弧

外側の [ ] が文字指定の角括弧で、内側の [: :] がクラス名の記法です。[:upper:] だけを書くと、:、u、p、e、r のどれか1文字を指定する意味になるので、[[:upper:]] と二重に書きます。

大文字(upper)

[[:upper:]]* は、先頭が大文字の名前に一致します。

echo[[:upper:]]*大文字1文字任意の文字列一致した名前(出力の順)Backup111Backup222Backup333IMAGEIMAGE1IMAGE12
図15 [[:upper:]]* は大文字で始まる名前に一致する
hirotano@linux-1:~$ echo [[:upper:]]*
Backup111 Backup222 Backup333 IMAGE IMAGE1 IMAGE12

小文字(lower)

*[[:lower:]] は、末尾が小文字の名前に一致します。

echo*[[:lower:]]任意の文字列小文字1文字一致した名前(出力の順)1–music11–music2–musicfile2.txt
図16 *[[:lower:]] は小文字で終わる名前に一致する
hirotano@linux-1:~$ echo *[[:lower:]]
1-music 11-music 2-music file2.txt

数字(digit)

[[:digit:]]* は、先頭が数字の名前に一致します。

echo[[:digit:]]*数字1文字任意の文字列一致した名前(出力の順)1–music11–music2–music
図17 [[:digit:]]* は数字で始まる名前に一致する
hirotano@linux-1:~$ echo [[:digit:]]*
1-music 11-music 2-music

まとめ

パス名展開では、*、?、[ ] を組み合わせて、名前のパターンを書きます。パターンはシェルが先に展開するので、コマンドには、一致した名前が並んで渡されます。今回の例を一覧にします。

パターン一致する名前例の結果
*任意の文字列(すべての名前)16個すべて
d*d で始まるdata1 data11 data111 dir1 dir11
*1111 で終わるBackup111 data11 data111 dir11
d*11d で始まり 11 で終わるdata11 data111 dir11
?????5文字IMAGE data1 dir11 file1
???11任意の3文字のあとに 11dir11
f*.???f で始まり、拡張子が3文字file2.txt
[ABC]*A、B、C のどれかで始まるBackup111 Backup222 Backup333
[a-f]*a から f のどれかで始まるdata1 data11 data111 dir1 dir11 file1 file2.txt
*[1-3][1-3][1-3]末尾が1から3の数字3つBackup111 Backup222 Backup333 data111
*[!1-3][1-3][1-3]末尾が「1〜3以外の1文字」+「1〜3の2文字」IMAGE12 data11 dir11
{file1,data*}ブレース展開(先に展開される)file1 data1 data11 data111
[[:upper:]]*大文字で始まるBackup111 Backup222 Backup333 IMAGE IMAGE1 IMAGE12
*[[:lower:]]小文字で終わる1-music 11-music 2-music file2.txt
[[:digit:]]*数字で始まる1-music 11-music 2-music

思ったとおりに展開されないとき

状態よくある原因確認すること
パターンがそのまま表示される一致する名前がない。または、パターンを引用符(' や ")で囲んでいるls で名前を確認する。引用符を外す。一致しないときに何も渡さない設定は shopt -s nullglob
. で始まる名前が出ない* や ? は、先頭のピリオドに一致しない.* のようにピリオドを書く。または shopt -s dotglob
大文字小文字が一致しないパス名展開は、大文字と小文字を区別する大文字小文字を区別しない設定は shopt -s nocaseglob
[a-f] で想定外の名前が出る範囲が、ロケールの照合順で解釈されているshopt globasciiranges を確認する。[[:lower:]] などの文字クラスを使う
サブディレクトリの中が出ない* は / に一致しないdir*/* のようにパスを区切って書く

コメント