ウェブサイト検索

このLinuxのワイルドカードの使い方を間違えると、データ損失のリスクがあります


Linuxのワイルドカードを使うと、単一のコマンドで同時に複数のファイルに作用させることができます。これは素晴らしい時間の節約になりますが、物事がうまくいかない場合もあります。そして、それは破壊的に起こり得ます。

ワイルドカードの目的

よく知られているワイルドカードは疑問符「?」とアスタリスク「*」です。これらはファイル名のパターンを作成するために使用できます。疑問符は任意の単一の文字を表し、アスタリスクはゼロ文字を含む任意の文字の配列を表します。

これを知っていれば、複数のファイル名に一致するパターンを構築できます。コマンドラインにすべてのファイル名を入力する代わりに、パターンを入力します。パターンに一致するすべてのファイルがコマンドによって処理されます。

このようなディレクトリにファイルのコレクションがある場合:

私たちは提供するパターンに一致するファイルのグループを選択できます。

ls taf_* 

それにより、名前の先頭に「taf_」が付くすべてのファイルが得られます。

ls *.sh
ls s*.sh

最初のコマンドはディレクトリ内のすべてのシェルスクリプトファイルをリストします。2番目のコマンドは、シェルスクリプトファイルでもある「s」で始まるファイルのみをリストします。

それはすべて簡単に思えますし、lsではそうです。しかし、他のコマンドはこの種のパターンマッチングを利用することができます。問題は、シェルがコマンドが機会を得る前にパターンマッチングを試みることで発生します。

findコマンドでアスタリスクを使用する

パターンを一致するファイルのリストに展開する行為はグロビングと呼ばれています。

それはUnixバージョン6のスタンドアロンコマンドとして始まり、その後、他のプログラムにリンクできるライブラリになり、現在ではシェルのビルトインになっています。パターンの展開はシェルによって行われ、その展開の結果はコマンドラインパラメータとしてコマンドに渡されます。

findコマンドを使用した2つの例を見てみましょう。一つはあなたが期待することをしますが、もう一つはあなたを驚かせるかもしれません。

この例では、readme.txtという単一のファイルを含むディレクトリを使用します。srcとincの2つのディレクトリがあります。それらはC、H、MD、TMPファイルの混合を含んでいます。

ls -R

findを使用して、パターン(-name *.c)に一致する名前のファイル(-type f)を再帰的に見つけることができ、Cファイルのリストを得ることができます。

find . -type f -name *.c

検索を反転させるために -not オプションを追加することができ、Cファイル 以外 のすべてを表示します。

find . -type f -not -name *.c

このリストを見直した結果、Cファイルを除いてすべて削除することに決めました。これを行うには、-deleteオプションを追加します。

find . -type f -not -name *.c -delete
find .

2番目のfindコマンドは、現在のディレクトリ内およびその下にあるすべてのファイルを再帰的にリストします。残っているのは私たちのCファイルだけです。

それは私たちのほとんどが予想していた通りに機能しました。さて、今回は全く同じことをしますが、今度は現在のディレクトリ内のファイルがテキストファイルではなく、Cファイルです。

ls -R

同じfindコマンドとオプションを使用してCファイル以外のすべてを削除します。それは私たちが全く望んでいたことではありません。

find . -type f -not -name *.c -delete
find .

それは、現在のディレクトリにある1つのCファイルを除いて、ディレクトリツリー内のすべてのファイルを悠然と削除しました。

私たちはもう一度ファイルをリセットし、私たちが使うべき方法でコマンドを発行します。

すべてのファイルは適切に配置されており、現在のディレクトリには以前と同様にCファイルがあります。

ls -R

今回は、ワイルドカードパターンを単一引用符で囲みます。

find . -type f -not -name '*.c' -delete
find .

それが私たちが望んでいたことです。私たちのCファイルを除いて、すべてが失われました。

では、何がうまくいかなかったのか?

シングルクォートはシェルがファイル名パターンを展開するのを止めます。それはそのままコマンドまたはプログラムに渡され、コマンドがそれに対して操作を行います。

うまくいった例では、現在のディレクトリにreadme.txtファイルがありました。シェルは*.cの一致を見つけられなかったため、*.cをfindに渡して処理させました。

すべてのファイルを削除し、Cファイルのみを残す例では、現在のディレクトリに main.c というファイルがありました。シェルはそのファイルにパターンを一致させ、そのファイルの名前を find コマンドに渡しました。したがって、find の指示は、main.c という名前のファイル以外のすべてを削除することでした。

コマンドラインパラメータをターミナルウィンドウに表示すること以上のことはしない小さなCプログラムでこれを示すことができます。

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) 
{
    int i;
    printf("You supplied %d arguments.\n", argc-1);
    for (i=1; i<argc; i++)
            printf("%-2d) \"%s\"\n", i, argv[i]);
    exit (0);
}

私はこれをglob.cという名前のファイルとして保存し、次のようにコンパイルしました。

gcc -o glob glob.c

変数argcは、プログラムに渡す引数の数を保持しています。forループが引数のリストを順に実行し、各引数をターミナルウィンドウに表示します。

forループは引数1から始まり、0ではありません。引数0があります。それは常にバイナリ自体の名前を保持します。混乱を避けるために、それを印刷するのは避けました。印刷される引数は、コマンドラインで提供するものだけです。

./glob one two 3 ant beetle cockroach

コマンドラインパラメータとして *.c でそれを試してみましょう。

ls *.c
./glob *.c

現在のディレクトリにCファイルがない場合、シェルは*.cをfindコマンドに渡します。findコマンドはワイルドカードパターンそのものに作用します。しかし、現在のディレクトリにCファイルがあると、シェルは一致するCファイルの名前をプログラムに渡します。

ls *.c
./glob *.c

私たちのプログラムはCファイルの名前をパラメータとして受け取り、findコマンドも同様です。したがって、実際にはfindは指示されたことを行っていました: main.cファイルを除くすべてのファイルを削除することです。

今回は、ワイルドカードパターンをシングルクォートで囲みます。

ls *.c

./glob '*.c'

シェルはワイルドカードパターンに自分のグロビングを適用する機会を無視し、それをコマンドに直接渡してさらなる処理を行います。

簡単な修正、私を引用してもいいよ

一般的なルールとして、findのようなコマンドに渡すワイルドカードパターンは引用符で囲むべきです。これがあれば、このような潜在的に破壊的なミスを防ぐことができます。

関連記事: