読者です 読者をやめる 読者になる 読者になる

ワイルドカード指定 "*" ではドットファイルは対象にならない

知ってるようで知らなかったのでメモ。

結論

コマンド (ls/rm/cp/etc) のワイルドカード指定 "*" では、ドットファイル*1の名前が展開されない。
ドットファイルを指定する場合は、 ".*" のようにしなければならない。

参考 連載UNIXの教科書〜ワイルドカードを知る〜 | 日本ヒューレット・パッカード

環境

確認

ホームディレクトリ配下に、下記のような構成で確認用のディレクトリを作成。

[~/tmp] $ find .  
.
./parent
./parent/child
[~/tmp] $ 

"child" ディレクトリに移動し、"ls -la" で内容確認。

[~/tmp/parent/child] $ ls -la
total 0
drwxr-xr-x  2 y_sumida  staff   68  9 30 21:58 .
drwxr-xr-x  3 y_sumida  staff  102  9 30 21:58 ..
[~/tmp/parent/child] $ 

ドットファイル、通常ファイル、ドットディレクトリ*2を作成。

[~/tmp/parent/child] $ touch .dotfile
[~/tmp/parent/child] $ touch normalfile
[~/tmp/parent/child] $ mkdir .dotdir

"ls -la" で内容確認。ドットファイル、ドットディレクトリも表示されます。

[~/tmp/parent/child] $ ls -la
total 0
drwxr-xr-x  5 y_sumida  staff  170  9 30 22:09 .
drwxr-xr-x  3 y_sumida  staff  102  9 30 21:58 ..
drwxr-xr-x  2 y_sumida  staff   68  9 30 22:09 .dotdir
-rw-r--r--  1 y_sumida  staff    0  9 30 22:06 .dotfile
-rw-r--r--  1 y_sumida  staff    0  9 30 22:06 normalfile
[~/tmp/parent/child] $ 

ここから本題。
"ls -la *" で確認すると、ドットファイル、ドットディレクトリが表示されません。
また、カレントディレクトリと親ディレクトリを表す、"." と ".." も表示されません。

[~/tmp/parent/child] $ ls -la *
-rw-r--r--  1 y_sumida  staff  0  9 30 22:06 normalfile
[~/tmp/parent/child] $ 

通常ファイル、ドットファイルに共通の末尾文字列 "file" をワイルドカード指定。
やはり、通常ファイルしか表示されません。

[~/tmp/parent/child] $ ls -la *file
-rw-r--r--  1 y_sumida  staff  0  9 30 22:06 normalfile
[~/tmp/parent/child] $ 

"ls -la .*" で確認。
ドットファイルは表示されましたが、なんか表示が変。
親ディレクトリの内容まで表示されています。
親ディレクトリを表す ".." がワイルドカードに引っかかったためでしょう。

[~/tmp/parent/child] $ ls -la .*
-rw-r--r--  1 y_sumida  staff  0  9 30 22:06 .dotfile

.:
total 0
drwxr-xr-x  5 y_sumida  staff  170  9 30 22:18 .
drwxr-xr-x  3 y_sumida  staff  102  9 30 21:58 ..
drwxr-xr-x  2 y_sumida  staff   68  9 30 22:09 .dotdir
-rw-r--r--  1 y_sumida  staff    0  9 30 22:06 .dotfile
-rw-r--r--  1 y_sumida  staff    0  9 30 22:06 normalfile

..:
total 0
drwxr-xr-x  3 y_sumida  staff  102  9 30 21:58 .
drwxr-xr-x  3 y_sumida  staff  102  9 30 21:58 ..
drwxr-xr-x  5 y_sumida  staff  170  9 30 22:18 child

.dotdir:
total 0
drwxr-xr-x  2 y_sumida  staff   68  9 30 22:09 .
drwxr-xr-x  5 y_sumida  staff  170  9 30 22:18 ..
[~/tmp/parent/child] $ 

コマンド固有ではないことの確認。

[~/tmp/parent/child] $ rm -i *
remove normalfile? n
[~/tmp/parent/child] $ rm -i .*
rm: "." and ".." may not be removed
rm: .dotdir: is a directory
remove .dotfile? n
[~/tmp/parent/child] $ 

ワイルドカードの展開結果の確認。
操作の対象ファイルを知りたかったらこれで分かります。

[~/tmp/parent/child] $ echo *
normalfile
[~/tmp/parent/child] $ echo .*
. .. .dotdir .dotfile
[~/tmp/parent/child] $ 

ワイルドカードの展開はコマンド個別に行うのではなくシェルが展開し、結果をコマンドに渡します。

例えば、vim が異常終了して、".ファイル名.swp" ファイルが大量にできてしまったので削除したい場合

こんな感じ。

rm .*.swp
rm `find . -name ".*.swp"`
find . -name ".*.swp" | xargs rm

ちなみに、"find" の "-name" オプションでワイルドカード指定する際に、"" が必要なのは、検索条件としてのワイルドカードを渡す必要があるためで、展開結果を渡してしまうと "unknown option" で怒られます。*3

[~/tmp/parent/child] $ find . -name .*
find: ..: unknown option
[~/tmp/parent/child] $ find . -name ".*"
.
./.dotdir
./.dotfile
[~/tmp/parent/child] $ 

もちろん、展開結果が 1 ファイルの場合は、問題ありません。

[~/tmp/parent/child] $ echo normal*
normalfile
[~/tmp/parent/child] $ find . -name normal*
./normalfile
[~/tmp/parent/child] $ 

感想

こういうの調べるの楽しいですね。

ワイルドカードでファイル名を指定するというのはよくやりますが、意外と知らないことが多くて驚きでした。
"ls -la *.swp" でドットファイルが対象にならないという現象を教えてもらった時は、そんなはずないよなーとか思ってました。

"*" でドットファイルが対象にならないのは、ドットファイル = 特殊ファイルという扱いなので簡単に消したりできないためかなーとか、カレントディレクトリ/親ディレクトリが削除対象になっちゃまずいからかなーとか考えましたが、歴史的経緯とか調べた訳ではないのでよくわかりませんでした。

説明おかしいぞ!とかあれば指摘お願いします。

*1:ファイル名がドットで始まる特殊ファイル

*2:呼び方合ってるかは分かりません。ディレクトリもファイルなのでドットファイルの一種でよいかも。

*3:参考 http://x68000.q-e-d.net/~68user/unix/pickup?%A5%D5%A5%A1%A5%A4%A5%EB%A5%B0%A5%ED%A5%D6