業務で楽するためのUNIXテクニック集「検索」編
この記事は2008年~2009年頃に翔泳社で連載していた「業務で楽するためのUNIXテクニック集」の原本です。
この連載では、UNIX系OSをコマンドライン上から効率よく利用するために知っておくべきテクニックや、便利な小技を紹介していきます。
今回は「ファイル・ファイル内容の検索」に関するテクニックを紹介します。
スポンサードリンク
findコマンドを利用したいくつかの例を紹介しておきます。なお、ここに紹介した書き方以外にも、様々な書き方があります。業務に併せて応用しながら利用してください。
Emacsを利用していると、テキストを保存する際に「ファイル名~」、ファイルを編集する際に「#ファイル名#」というバックアップファイルが作成されます。また、Unix・Linuxプロセスが異常終了すると「core」ファイルが生成されます。これらのファイルを一度に削除したい場合には、次のように指定できます。
$ find . \( -name core -o -name '#*' -o -name '*~' \) -exec rm '{}' +
「\(」と「コマンド」、「コマンド」と「\)」の間にはスペースを空ける必要があります。
xargsコマンドを利用する場合は、次のように指定できます。
$ find . \( -name core -o -name '#*' -o -name '*~' \) -print0 | xargs -0 rm
findコマンドの判別式「-o」は、「-or」でも構いません。
なお、findコマンドの「-delete」アクションが利用できる場合は、次の方法が最も高速です。
$ find . \( -name core -o -name '#*' -o -name '*~' \) -delete
$ find . -type f -user $(whoami) -exec rm '{}' +
xargsコマンドを利用する場合は、次のように指定できます。
$ find . -type f -user $(whoami) -print0 | xargs -0 rm
whoamiコマンドは、ユーザ名を表示します。
検索して見つかった全ファイルを「/var/tmp/」以下に移動します。
$ find . -type f -print0 | xargs -0 mv -t /var/tmp/
mvコマンドの「-t」または「--target-directory」オプションで、移動先ディレクトリを指定できます。なお、この例ではディレクトリ構造を維持せず、ファイルのみ移動します。同名のファイルが存在すると上書きされるなど期待通りに動かないため、注意が必要です。
※注4 xargsコマンドに「-i」(FreeBSDでは「-I」)オプションをつけると、パイプで渡された結果を「{}」で展開できます。このため、次の指定方法でも動作します。 $ find . -type f -print0 | xargs -0 -i mv '{}' /var/tmp/ FreeBSDの場合、デフォルトで「{}」で展開できないため、次のように指定します。 $ find . -type f -print0 | xargs -0 -I % mv % /var/tmp/ 「-I」オプションの後の文字(例では「%」)を、findコマンドで見つかったファイル名に置換しています。 ただしこの方法は、1ファイルづつmvコマンドを実行するため、処理が遅くなります。
※注5 FreeBSDでは、xargsコマンドの「-J」オプションを利用すると、「-I」オプションより高速に処理できます。 $ find . -type f -print0 | xargs -0 -J % mv % /var/tmp/ この例では、検索して見つかったファイル名のグループを「%」に置き換えてmvコマンドを実行しています。
検索して見つかった全ファイルを、ディレクトリ構造を保持したまま「/var/tmp/」以下にコピーします。これは、ファイル単位でバックアップを取る場合に、役に立ちます。ファイルのバックアップで利用するcpioコマンドを利用すると次のように指定できます。
$ find . -type f -print0 | cpio -pd0 /var/tmp/
「-p」オプションは、ファイルを別のディレクトリにコピーします。「-d」オプションは、必要に応じてディレクトリを作成します。なお、cpioコマンドの「-0」オプションはGNUのcpioコマンド以外では使えません。
「~/public_html」以下で、拡張子が「.gif」かつサイズが100Kバイト以上のファイルを探し、結果を「result.txt」ファイルに書き込みます。
$ find ~/public_html \( -name "*.gif" -a -size +100k \) -fprint result.txt
一般ユーザーで、ルートディレクトリや「/etc」ディレクトリなどを対象に検索すると「Permission denied」という警告メッセージが大量に表示されます。検索結果の一覧が警告メッセージに埋まってしまい、検索結果の確認が大変な場合には、次の方法で表示されなくなります。
$ find / -name user → 「user」を検索 find: /usr/src: Permission denied → 警告メッセージ find: /usr/share/skel/MailBox: Permission denied → 警告メッセージ find: /usr/obj: Permission denied → 警告メッセージ (略) /home/user $ find / -name user 2>/dev/null /home/user
「2>/dev/null」を指定して、警告メッセージをヌルデバイスへ出力しています。
findコマンドは、ディレクトリツリーの中からファイルを探し出すことができる、大変強力で複雑なコマンドです。
$ find [パス][式(オプション、判別式、アクション)]
「式」は、オプション、判別式およびアクションの組み合わせからなります。
※注1 findコマンドは、OSによって使用可能なオプションが異なります。 例えば、SolarisなどのOSでは、以降で紹介する例の多くが使用できません (参考ページ:「find」SunOSリファレンスマニュアル1)
findコマンドの代表的なオプションを表にまとめておきます。
findコマンドの代表的なオプション
オプション | 意味 |
---|---|
-follow | シンボリックリンクの参照先を検索する |
-maxdepth n | ディレクトリの深さを指定して検索する。n=0 ならサブディレクトリは検索しない |
-mindepth n | ディレクトリの深さを指定して検索する。n=1 なら指定したディレクトリ自身は検索せず、サブディレクトリ以下を検索する |
例)ルートディレクトリ「/」から、2 階層目までを検索して表示する
$ find / -maxdepth 2 / /home /home/yasuda (略) /usr /usr/local (略)
0、1階層目も表示されることに注意して下さい。
findコマンドの代表的な判別式を表にまとめておきます。
判別式 | 意味 |
---|---|
-name [pattern] | ファイル名が指定したパターンと一致すれば真。シェルのワイルドカードが指定できる |
-path [pattern] | パス名が指定したパターンと一致すれば真。シェルのワイルドカードが指定できる |
-regex [pattern] | ファイル名を正規表現で指定したパターンと一致すれば真 |
-type [filetype] | ファイルが指定したファイル種類と一致すれば真 |
d ディレクトリ | |
f 通常のファイル | |
l シンボリックリンク | |
-group [group] | ファイルの所有グループが、指定したグループと一致すれば真 |
-user [user] | ファイルの所有者が、指定したユーザであれば真 |
次のように、検索開始ディレクトリを複数指定して検索することができます。
例)「/usr/local/bin」「/usr/bin」「/bin」以下の全ファイルを検索する
$ find /usr/local/bin /usr/bin /bin -type f /usr/local/bin/perldoc /usr/local/bin/perlivp (略)
判別式 | 意味 |
---|---|
-atime | 最終アクセス日がn日前なら真 |
+nの場合:n日より大きい | |
-nの場合:n日より小さい | |
-mtime | (+/-)n ファイルの最終更新日付がn日前なら真 |
+nの場合:n日より大きい | |
-nの場合:n日より小さい | |
-mmin | (+/-)n ファイルの最終更新日時n分前なら真 |
+nの場合:n日より大きい | |
-nの場合:n日より小さい | |
-newer [file name] | ファイルの最終更新日付が、指定したファイルの最終更新日付より新しければ真 |
例)ファイルの最終更新日が3日より前のファイルを検索する
$ date Fri Nov 21 13:48:19 JST 2008 $ ls -l total 0 -rw-r--r-- 1 root root 0 Nov 20 13:30 test1.txt -rw-r--r-- 1 root root 0 Nov 19 13:30 test2.txt -rw-r--r-- 1 root root 0 Nov 18 13:30 test3.txt $ find . -mtime +3 ./test3.txt
判別式 | 意味 |
---|---|
-empty | ファイルが空なら真 |
-size n[c/k/b] | nのサイズのファイルであれば真 |
c バイト | |
k キロバイト | |
b ブロック(1ブロック=512バイト) |
判別式 | 意味 |
---|---|
-perm mode | ファイルのアクセス権がmodeと一致すれば真 |
-perm -mode | modeで指定されているアクセス権の全てが許可されていれば真 |
-perm +mode | modeで指定されているアクセス権の一部が許可されていれば真 |
次の例では、カレントディレクトリ以下にある、アクセス属性が644(-rw-r--r--)のファイルを検索・表示します。
例)ファイルのアクセス権限(パーミッション)でファイルを検索する
$ find . -perm 644 ./.cshrc ./.login ./.login_conf (略)
判別式は次のような演算子で条件を追加できます。
演算子 | 機能 |
---|---|
\( 判別式 \) | 括弧のなかを優先的に判別する |
! 判別式 | 判別式が異なる場合、検索対象となる。「-not 判別式」も同じ |
判別式1 -a 判別式2 | 判別式1と判別式2をandで評価する。「判別式1 -and 判別式2」「判別式1 判別式2」も同じ |
判別式1 -o 判別式2 | 判別式1と判別式2をorで評価する。「判別式1 -or 判別式2」 も同じ |
アクションは次のものが指定可能です。
アクション | 機能 |
---|---|
検索結果を標準出力する。このとき結果はフルパスで表示される。デフォルトなので省略可能 | |
-fprint ファイル名 | 検索結果を「ファイル名」に出力する。このとき結果はフルパスで表示される |
-exec コマンド '{}' \; | 検索後、コマンドを実行する。このとき 「{}」が検索されたファイル名に置き換えられる。最後は「\;」のようにエスケープシーケンスを使用する必要がある |
-exec コマンド '{}' + | 検索後、コマンドを実行する。 このとき、「{}」がファイル名のグループに置き換えられる |
-ok コマンド \; | -execと同様に検索後コマンドを実行する。ただし、ユーザーに問い合わせる |
検索して見つかった全ファイルを一度に処理したい場合、findコマンドのアクションを利用して次のように指定できます。
$ find . -type f -exec コマンド '{}' \;
「-exec」アクションにより、検索結果に対して「コマンド」を実行します。このとき「{}」がfindコマンドの実行結果、つまりファイルのパスに置き換えられます。
ただし上記の例は、ファイル数分「コマンド」が実行されるので時間がかかります。このため、一般的にはxargsコマンドを用いて、次のように指定します(Solarisには対応していません)。
$ find . -type f -print0 | xargs -0 コマンド
xargsコマンドは、標準入力から引数を読み込み、指定の「コマンド」を実行します。上記の例では、findコマンドを利用して見つかった全ファイルのパスを、一度に「コマンド」へ渡しています。
なお、空白や特殊文字を含むファイルを正しく処理するため、findコマンドには必ず「-print0」オプションを付け「xargs -0」で受け取るようにしましょう。「-0」オプションは「--null」でも構いません(FreeBSDでは「-0」オプションのみ利用可能)。
これにより、findコマンドは空白と改行ではなくヌル文字(\0)を区切りとして、検索結果を出力します。同様にxargsコマンドも、引数がヌル文字で区切られているものとして処理を行います。
※注2 findコマンドの-execアクションは、シェルを起動しません。シェルコマンドを使いたい場合は、次のように指定します。 $ find … -exec sh -c 'シェルコマンド文字列' \;
※注3 xargsコマンドで一度に渡せる長さの制限はOSによって異なります。 制限値は「ARG_MAX」というマクロ定数で定義されています。 Debian の場合は、次のコマンドで自分の使っているOSのARG_MAXを 調べることができます。 Debian の場合のARG_MAX調査方法 $ getconf ARG_MAX 131072
最近のfindコマンド(GNU findの4.2.12以降)では、見つかったファイルを一度に処理するための「-exec コマンド '{}' +」オプションが存在します(FreeBSD、Solarisも対応)。
$ find . -type f -exec コマンド '{}' +
「+」を指定した場合、「{}」が、検索して見つかった全ファイル名のパスに置き換えられます。
xargsコマンドを使わない場合は、シェルの制御構文を使って次のように指定できます。
$ find . -type f -print0 | while read -r -d '' file; do コマンド "$file"; done
ファイル名に「\(バックスラッシュ)」が含まれている場合に備え、readに「-r」オプションを付けています。ただし、この方法も見つかったファイル数分「コマンド」を実行するので、速度は遅くなります。
例)上記のそれぞれの方法で、検索して見つかったファイルに対しlsコマンドを用いて一覧表示
$ ls test1.txt test2.txt test3.txt $ find . -type f -exec ls '{}' \; ./test1.txt ./test2.txt ./test3.txt $ find . -type f -print0 | xargs -0 ls ./test1.txt ./test2.txt ./test3.txt $ find . -type f -exec ls '{}' + ./test1.txt ./test2.txt ./test3.txt $ find . -type f -print0 | while read -r -d '' file; do ls "$file"; done ./test1.txt ./test2.txt ./test3.txt
このようにコマンドの組み合わせによって、「ファイル名のグループを一度にlsコマンドで実行」か「ファイル数分lsコマンドを実行」するかが異なり、処理速度が変わってきます。
今回はfindコマンドやgrepコマンド、そしてxargsコマンドを利用した「ファイル・ファイル内容の検索」に関するテクニックを紹介しました。今回紹介したほかにも、awkやperlを利用することで、簡潔な指定や高速な処理が可能な場合もあります。
次回以降も、テーマごとにUNIXテクニックを紹介していく予定です。
スポンサードリンク