globパターンを利用すると、「*」「?」「[]」などのワイルドカードや文字クラスを組み合わせて、多数のファイルパスをまとめて表記できる。
「glob」とは「*」「?」などのワイルドカードを使ったファイルパス(globパターン)から、それにマッチする実際のファイルパスを展開すること。
本はといえば、globはUNIXの初期バージョンに搭載され(/etc/globコマンド)、ファイル名展開を行うために使われていた。/etc/globが提供していた機能は後に、globライブラリ関数として提供されるようになり、各種のシェルで使われるようになった。
UNIXのglobでは以下のワイルドカードパターンや文字クラスを使用できる。
[]を利用した表記のことを「文字クラス」と呼ぶ。文字クラスでは、[abcdefg]のような記述と、[a-g]のような記述が可能である。後者はハイフン(-)を使い[abcdefg]と同じ範囲を表現している。つまり、両者は「abcdefgのいずれか1文字にマッチ」することになる。また、このときには「!」を使って範囲を反転することもできる。例えば、[!abc]は「abc以外のいずれか1文字にマッチ」する。
例として、macOSでのglobの利用例を以下に示す(タブ位置は適宜調整してある。以下、同様)。
$ ls …… 全てのファイルを一覧
abc.txt bbc.txt boo.txt foo.txt gooo.txt
$ ls ?bc* …… 先頭が任意の文字、その後に「bc」が続くもの
abc.txt bbc.txt
$ ls *oo.txt …… 最後が「oo」で拡張子が「.txt」になっているもの
boo.txt foo.txt gooo.txt
$ ls ?oo.txt …… 先頭が任意の文字、その後に「oo」が続く、拡張子が「.txt」のファイル
boo.txt foo.txt
$ ls [a-c]* …… ファイル名が「abc」のいずれかで始まるもの
abc.txt bbc.txt boo.txt
$ ls [!a]bc* …… ファイル名が「a」以外で始まり、次に「bc」が続くもの
bbc.txt
「*oo.txt」と「?oo.txt」の違いは、前者では「oo」の前は任意の長さの文字列がマッチするため、「gooo」もマッチするのに対して、後者では「oo」の前には任意の1文字のみがマッチするため、「gooo」はマッチしない点にある。
また、Windows(MS-DOS)のコマンドプロンプトでは、上記のうち、ワイルドカード(*と?)のみが使用できる。以下に例を示す(表示を簡潔にするために/bオプションを指定している)。文字クラス([])を使用できない点に注目しよう。
> dir /b …… 全てのファイルを一覧
abc.txt
bbc.txt
boo.txt
foo.txt
gooo.txt
> dir /b ?bc* …… 先頭が任意の文字、その後に「bc」が続くもの
abc.txt
bbc.txt
> dir /b *oo.txt …… 最後が「oo」で拡張子が「.txt」になっているもの
boo.txt
foo.txt
gooo.txt
> dir /b ?oo.txt …… 先頭が任意の文字、その後に「oo」が続く、拡張子が「.txt」のファイル
boo.txt
foo.txt
> dir /b [abc]*.txt …… []は使えない
ファイルが見つかりません
PowerShellでは[]を使用できる。ただし、「[!a]*」のように、文字クラスで指定した範囲を反転することはできない。以下に例を示す(空行は筆者が適宜削除している)。
> ls ?bc* …… 先頭が任意の文字、その後に「bc」が続くもの
ディレクトリ: C:\project\devbasics\glob
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2017/08/28 10:27 0 abc.txt
-a---- 2017/08/28 10:27 0 bbc.txt
> ls [abc]*.txt …… PowerShellでは[]が使える
ディレクトリ: C:\project\devbasics\glob
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2017/08/28 10:27 0 abc.txt
-a---- 2017/08/28 10:27 0 bbc.txt
-a---- 2017/08/28 10:27 0 boo.txt
> ls [!a]bc.txt …… ただし、[!……]は使えない
ディレクトリ: C:\project\devbasics\glob
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2017/08/28 10:27 0 abc.txt
ここまでに見てきたのはある意味では「古典的」なglobパターンとなる。最近では、.gitignoreファイルやVisual Studio Codeのsettings.jsonファイルなどで、上で見たglobパターンよりも柔軟な形でファイルパスを記述できるようになってきている。典型的なのが「**」を使ったものだ。これはいわゆる「globstar」と呼ばれるもので、ファイルパス中にこれが現れると「0個以上のディレクトリ/サブディレクトリ」にマッチするようになる。単独で使用した場合には、カレントディレクトリ以下の全てのファイル/ディレクトリにマッチする。
例えば、Visual Studio Codeの設定では[エクスプローラー]ビューに表示する/表示しないファイルの切り替えは次のようにして指定している(デフォルト値)。
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true
},
これはプロジェクトのトップレベルのディレクトリ以下にある全ディレクトリのうち、「.git」「.svn」「.hg」「.CVS」「.DS_Store」にマッチするものを意味する。そして、マッチしたファイル/ディレクトリは[エクスプローラー]ビューに表示されない。
ちなみにglobstarはBashなどのシェルでglobstarオプションを設定することで(shopt -s globstar)、シェルからでもglobstar表記を利用できるようになる。例えば、「ls **」などを実行すると、「カレントディレクトリ以下の全てのファイル、ディレクトリを全て一覧」してくれる。以下は、macOSにglobstarオプションを利用可能なBashをインストールして、「ls *」コマンドと「ls **」コマンドを実行したものだ。
$ ls * …… 「*」はカレントディレクトリ直下のファイルとディレクトリにマッチ
abc.txt bbc.txt boo.txt foo.txt gooo.txt
tmp:
bar.txt tmp2
$ ls ** …… 「**」はカレントディレクトリ以下の全ファイル/ディレクトリにマッチ
abc.txt foo.txt tmp/tmp2/baz.txt
bbc.txt gooo.txt
boo.txt tmp/bar.txt
tmp:
bar.txt tmp2
tmp/tmp2:
baz.txt
「ls *」コマンドでは、シェルが「*」を「5つのファイルとtmpディレクトリ」に展開してからlsコマンドに渡すので、それらが表示されるだけとなる。一方、「ls **」コマンドでは、「**」がカレントディレクトリ以下の全てのファイル/ディレクトリにマッチするので、上記の「5つのファイルとtmpディレクトリ」だけではなく、「tmpディレクトリにあるbar.txtファイルとtmp2ディレクトリ、tmp2ディレクトリにあるbaz.txtファイル」にもマッチしていることが分かる(ファイルの一覧に7つのファイルが表示され、tmp/tmp2ディレクトリの内容も表示されている点に注目)。
この他にも、Bashなどのシェルで採用されている「Extended Globbing」機能を利用できる場合もある(Bashで利用するには「shopt -s extglob」コマンドを実行する)。例えば、Node.jsで利用可能なnode-globパッケージではExtended Globbing機能が提供する以下のglobパターンを利用できる。
pattern-listには、これまでに見てきたようなパターンを記述できる。以下にnode-globパッケージの使用例を示す。
var glob = require("glob");
glob("?bc*", function(err, files) {
console.log("?bc*");
for (var item of files) {
console.log(item);
}
});
glob("!(abc)*.txt", function(err, files) {
console.log("!(abc)*");
for (var item of files) {
console.log(item);
}
});
glob("*([ab])bc*", function(err, files) {
console.log("*([ab])bc*");
for (var item of files) {
console.log(item);
}
});
glob("@(abc|bbc|foo).txt", function(err, files) {
console.log("@(abc|bbc|foo).txt");
for (var item of files) {
console.log(item);
}
});
glob関数では第1引数にglobパターンを指定し、コールバック関数でそのパターンにマッチしたファイル群を受け取り、そのファイル名を表示しているだけだ。このスクリプトの実行例を以下に示す。
$ ls *.txt
abc.txt bc.txt foo.txt
bbc.txt boo.txt gooo.txt
$ node hoge.js
?bc* …… 「?bc*」はabc.txt、bbc.txtにマッチする
abc.txt
bbc.txt
!(abc)*.txt …… 「!(abc)*」はabc.txt以外の全ての.txtファイルにマッチする
bbc.txt
bc.txt
boo.txt
foo.txt
gooo.txt
*([ab])bc* …… 「*([ab])bc*」はabc.txt、bbc.txt、bc.txtにマッチする
abc.txt
bbc.txt
bc.txt
@(abc|bbc|foo).txt …… 「@(abc|bbc|foo).txt」は以下の3つにマッチする
abc.txt
bbc.txt
foo.txt
また、このパッケージでは「{a, b}bc.txt」のような記述も可能だ。これは{}内にカンマ区切りで記述した要素が、それぞれ他の部分と連結される。従って、今示したパターンは「abc.txt」と「bbc.txt」に展開される。
globパターンを利用すると、「*」「?」「[]」などのワイルドカードや文字クラスを組み合わせて、多数のファイルパスをまとめて表記できる。最初のうちは、使いこなしが難しいかもしれないが、簡単なパターンから始めて、徐々にその意味するところを体得していけば、最終的にはExtended Globbingで提供される拡張パターンマッチ機能なども使いこなせるようになるだろう。
Copyright© Digital Advantage Corp. All Rights Reserved.