普段から使ってるコマンドのうち、 一度覚えてしまったがために他の使い方を覚えられないコマンドの1つ。 前回の Vimで全体での文字列置き換え と同じような話。 ついでにfindについてちょっとしたまとめ。
findコマンド
特定のディレクトリ以下で特定の名前のファイルやディレクトリを探すコマンド。
$ find .
とすれば、カレントディレクトリ以下の全てのファイルとディレクトリが表示されます。
ファイル名を指定して検索
恐らく一番最初に覚えるものだと思いますが、-name
を使うと
指定された名前にマッチするファイル/ディレクトリだけを表示します。
$ find . -name "*.md"
*
などのシェルスクリプトで使うような正規表現も一部使えます。
文字だけなら"
で囲う必要は無いですが、正規表現を使ったりしたい場合は
囲う必要があります。
-name
を-iname
とすると大文字小文字の区別をしなくなります。
なんですが、何故かこれを覚える前に、
$ find . | grep "md"
みたいにしてしまうことを覚えてずっと-name
を使ってなかったので
いい加減覚えて正しく使おう、と言うのが今回の件。
複数の名前を指定
-name
を使って名前を指定する時、複数の名前を指定するときは
$ find . -name "*.md" -o -name "*.txt"
の様に、各条件を-o
(-or
)または-a
(-and
)でつなげます。
さらに複雑な指定をしたい時なんかは\(
/\)
で括弧で囲って
優先順位を決めたりも出来ます。
指定の名前を除外
否定(-not
)を使えば特定の名前以外を検索できます。
$ find . -not -name "test.*" -a -name "*txt"
で.txt
という拡張子を持つファイルのうちtest.txt
(もしくtest.aaa.txt
等)以外の物を検索します。
パイプを使ってxargsで結果を渡す
findを使って良く行うのが検索結果を対象に何かコマンドを実行すること。
$ find . -name "*txt" | xargs ls
とすればfindで見つかったものに対してls
を行います。
ただ、これだと結果に出るファイルやディレクトリに空白があったりすると
その空白で区切った物がそれぞれ1つとしてls
の引数に渡されてしまいます。
試しに、空白のあるファイルを作って
$ vim "a b.sh"
この中身をこんな感じにします。
1 2 3 4 |
|
このファイルがあるディレクトリでfind
を実行してxargs
でこのスクリプトに渡してみると
$ find . | xargs ./a\ b.sh
$1=.
$2=./a
$3=b.sh
$4=
こんな感じでa
とb.sh
の部分が分離してしまいます。
これを避けるためには、find
へ-print0
と言うオプションを渡し
xargs
に-0
というオプションを渡します。
$ find . -print0 | xargs -0 ./a\ b.sh
$1=.
$2=./a b.sh
$3=
$4=
-print0
でfindの結果をNULLで区切る様になります。
xargs
の方の-0
はインプットの区切りをNULL文字とします(デフォルトは空白と改行)。
-execでコマンドを実行
パイプ+xargs
を使わずに、-exec
オプションを使うと
find
の引数として直接見つかったファイルを
コマンドに渡す事も出来ます。
$ find . -exec ./a\ b.sh {} +
$1=.
$2=./a b.sh
$3=
$4=
-exec
に続いてコマンドを書いて、その後の{}
の部分が
find
で見つかったファイルに展開されます。
ここで、最後の+
が、見つかったファイルを一括でコマンドに渡す、
つまり、上のパイプ+xargs
と同等の状況にするようにするためのオプションです。
これを最後\;
とすると、
$ find . -exec ./a\ b.sh {} \;
$1=.
$2=
$3=
$4=
$1=./a b.sh
$2=
$3=
$4=
の様になります。これは、見つかった物一つ一つをa b.sh
へ渡して
ファイルの数だけコマンドを実行します。