Photo by Agence Olloweb on Unsplash
はじめに こんにちは、中本です。シェルスクリプト連載 の3日目です。
本記事ではShell Script作成において切っても切れない find
コマンド について利用頻度高めのオプションをまとめます。
最後には今回紹介したオプションを全て盛り込んだシェルスクリプトを作成します。
目次
findとは
様々なfindオプション
-typeオプション:検索対象のファイル種別を指定
おまけ
おわりに
find
とは「find
」は、場所を指定してファイルやディレクトリを検索するコマンドです。
「ファイル名/ディレクトリ名」「更新日時」「種別」など様々な条件を指定してファイルを検索できます。
様々なfind
オプション -type
オプション検索する対象のファイル種別を指定するオプション
$ find [検索パス]-type f $ find [検索パス]-type d
$ ls -l total 0 drwxrwxr-x 2 finduser finduser 6 Mar 29 09:00 dir1 drwxrwxr-x 2 finduser finduser 6 Mar 29 09:00 dir2 drwxrwxr-x 2 finduser finduser 6 Mar 29 09:00 dir3 -rwxrwxr-x 1 finduser finduser 0 Mar 29 09:00 file01.tmp -rwxrwxr-x 1 finduser finduser 0 Mar 29 09:00 file02.tmp -rwxrwxr-x 1 finduser finduser 0 Mar 29 09:00 file03.tmp $ find ./ ./ ./dir1 ./dir2 ./dir3 ./file01.tmp ./file02.tmp ./file03.tmp $ find ./ -type f ./file01.tmp ./file02.tmp ./file03.tmp $ find ./ -type d ./ ./dir1 ./dir2 ./dir3
‐name
オプション検索する対象の検索文字列を指定するオプション ワイルドカードを使用することで、部分一致のファイルやディレクトリの検索が可能となります。
$ find [検索パス]-name "[検索文字列]"
$ find . -type f -name "file01" $ $ find . -type f -name "*.tmp" ./file01.tmp ./file02.tmp ./file03.tmp
なお、「-path
」も同様に検索文字列を指定するオプション 「-name
」と異なり、「/」を含む文字列検索が可能です。
$ ls -l ./dir1/ total 0 -rw-r--r-- 1 root root 0 Mar 29 02:24 file01.tmp $ find . -type f -name "*dir1/file01.tmp" "find: warning: Unix filenames usually don't contain slashes (though pathnames do). That means that '-name ‘*dir1/file01.tmp’' will probably evaluate to false all the time on this system. You might find the '-wholename' test more useful, or perhaps '-samefile'. Alternatively, if you are using GNU grep, you could use 'find ... -print0 | grep -FzZ ‘*dir1/file01.tmp’'." $ find . -type f -path "*dir1/file01.tmp" ./dir1/file01.tmp
-mtime
/-mmin
オプションファイルやディレクトリのタイムスタンプから判定して、検索対象期間を指定するオプション
$ find [検索パス] -mtime 日数 $ find [検索パス] -mtime -日数 $ find [検索パス] -mtime +日数
上記のように「+」や「‐」を付与することで検索対象期間をより特定期間に絞ることが可能となります(※対象期間については後述)。 日数は今日が「0」で、昨日が「1」と換算します。
$ ls -l ./ -rw-r--r-- 1 finduser finduser 0 Mar 25 01:00 test01.txt -rw-r--r-- 1 finduser finduser 0 Mar 26 01:00 test02.txt -rw-r--r-- 1 finduser finduser 0 Mar 27 01:00 test03.txt -rw-r--r-- 1 finduser finduser 0 Mar 28 01:00 test04.txt -rw-r--r-- 1 finduser finduser 0 Mar 29 01:00 test05.txt $ find . -mtime +1 ./test01.txt ./test02.txt ./test03.txt $ find . -mtime 1 ./test04.txt $ find . -mtime -1 ./test05.txt
また、オプション無しの状態ではコマンド実行時点を起点として、日数計算を行いますが、「-daystart
」オプションを付与することで当日24:00を起点として日数計算を行います。 (ジョブ処理等で、コマンド実行時間によって処理にばらつきを生じさせたくない際などに利用推奨)
他にも、「-mtime
」ではなく、「‐mmin
」も存在し、分単位で指定することも可能です。
「-mtime
」「-mmin
」はfind
を利用する上で高頻度で利用されるオプションですが、対象となる期間がややこしいので、以下のように整理してみました。
注意点としては、「-daystart
」オプションは日の始まりである0:00ではなく、日の終わりの24:00を起点としていること注意が必要です。 そのため「-mtime -0
」は必ず未来日検索となるため、基本的にファイル検索結果は存在しません。
-depth
オプション検索対象ディレクトリの階層を指定するオプション
本オプションを指定しない場合、子ディレクトリ全てに対して検索を実行します。
ファイル数が膨大にあり、検索対象のディレクトリ階層を絞りたい時などに有効です。
$ find [検索パス]-maxdepth [階層] $ find [検索パス]-mindepth [階層]
$ tree ./find_test1 ./find_test1/ | 20210328.tmp ∟ find_test2 | 20210328.tmp ∟ find_test3 ∟ 20210328.tmp $ find /work/find_test1-type f -name "*.tmp" /work/find_test1/find_test2/find_test3/20210328.tmp /work/find_test1/find_test2/20210328.tmp /work/find_test1/20210328.tmp $ find /work/find_test1 -maxdepth 1 -type f -name "*.tmp" /work/find_test1/20210328.tmp $ find /work/find_test1 -maxdepth 2 -type f -name "*.tmp" /work/find_test1/find_test2/20210328.tmp /work/find_test1/20210328.tmp $ find /work/find_test1 -maxdepth 3 -type f -name "*.tmp" /work/find_test1/find_test2/find_test3/20210328.tmp /work/find_test1/find_test2/20210328.tmp /work/find_test1/20210328.tmp $ find /work/find_test1 -mindepth 1 -type f -name "*.tmp" /work/find_test1/find_test2/find_test3/20210328.tmp /work/find_test1/find_test2/20210328.tmp /work/find_test1/20210328.tmp $ find /work/find_test1 -mindepth 2 -type f -name "*.tmp" /work/find_test1/find_test2/find_test3/20210328.tmp /work/find_test1/find_test2/20210328.tmp $ find /work/find_test1 -mindepth 3 -type f -name "*.tmp" /work/find_test1/find_test2/find_test3/20210328.tmp
‐exec
オプションコマンド実行結果を引数として次の処理に引き渡す場合などに利用されます。 同様の動きを持つコマンドとして「xargs
」があります。
$ find . -type f -name "*.txt" -exec ls -l {} \; -rw-r--r-- 1 finduser finduser 0 Mar 25 01:00 ./test01.txt -rw-r--r-- 1 finduser finduser 0 Mar 26 01:00 ./test02.txt -rw-r--r-- 1 finduser finduser 0 Mar 27 01:00 ./test03.txt -rw-r--r-- 1 finduser finduser 0 Mar 28 01:00 ./test04.txt -rw-r--r-- 1 finduser finduser 0 Mar 29 01:00 ./test05.txt $ find . -type f -name "*.txt" |xargs ls -l -rw-r--r-- 1 finduser finduser 0 Mar 25 01:00 ./test01.txt -rw-r--r-- 1 finduser finduser 0 Mar 26 01:00 ./test02.txt -rw-r--r-- 1 finduser finduser 0 Mar 27 01:00 ./test03.txt -rw-r--r-- 1 finduser finduser 0 Mar 28 01:00 ./test04.txt -rw-r--r-- 1 finduser finduser 0 Mar 29 01:00 ./test05.txt
一見すると、xargs
も-exec
も同様の処理結果を返すように見えるのですが、
$ find . -type f -name "*.txt" -exec echo "ファイル名: {}" \; ファイル名: ./test01.txt ファイル名: ./test02.txt ファイル名: ./test03.txt ファイル名: ./test04.txt ファイル名: ./test05.txt $ find . -type f -name "*.txt" |xargs echo "ファイル名: " ファイル名: ./test01.txt ./test02.txt ./test03.txt ./test04.txt ./test05.txt
上記の挙動で分かるように、以下のように処理に違いがあります。
したがって、検索したファイルを1ファイル単位で圧縮するなどの処理の場合は、-exec
オプションを利用が推奨されます。
while read line
while read line
に関しては、find
のオプションではありませんが、find
コマンドと相性の良いループ処理です。 検索結果を一行ずつ読み込んで、任意の処理を実行できます。
while_read_line.sh #!//bin/bash ${FIND_DIR} ="[検索パス]" find ${FIND_DIR} -type f -name "*.txt" |while read line do echo "ファイル名: $line " ls -l $line done exit
$ bash ./while_read_line.sh ファイル名: ./test01.txt -rw-r--r-- 1 finduser finduser 0 Mar 25 01:00 ./test01.txt ファイル名: ./test02.txt -rw-r--r-- 1 finduser finduser 0 Mar 26 01:00 ./test02.txt ファイル名: ./test03.txt -rw-r--r-- 1 finduser finduser 0 Mar 27 01:00 ./test03.txt ファイル名: ./test04.txt -rw-r--r-- 1 finduser finduser 0 Mar 28 01:00 ./test04.txt ファイル名: ./test05.txt -rw-r--r-- 1 finduser finduser 0 Mar 29 01:00 ./test05.txt
while read line
はもちろんfind
のみではなく、
cat {ファイル名} |while read line
echo {変数} |while read line
といった形でも利用可能です。
おまけ 先で紹介したオプションを盛り込んで、
【-mtime】前日以前に更新されたファイル 【-name】ファイル名に「ccc」を含む 【-type】検索種別は「ファイル」 【-maxdepth】検索対象ディレクトリは第2階層目まで 【while read line】各ファイルごとに圧縮する
処理を行うシェルスクリプトを作成してみます。
find.sh #!//bin/bash FIND_DIR="/work/find_test1/test" find /work/find_test1/ -maxdepth 2 -type f -name "*ccc*.tmp" -mtime +1 |while read FILE_PATH do FILE_DIR=`dirname ${FILE_PATH} ` FILE_NAME=`basename ${FILE_PATH} ` sed -e "/^bbb$/a find.shにより特定行を挿入" ${FILE_DIR} /${FILE_NAME} > ${FILE_DIR} /${FILE_NAME} .tmp mv ${FILE_DIR} /${FILE_NAME} .tmp ${FILE_DIR} /${FILE_NAME} chmod 775 ${FILE_DIR} /${FILE_NAME} chown finduser:finduser ${FILE_DIR} /${FILE_NAME} tar -C ${FILE_DIR} -cvzf ${FILE_DIR} /${FILE_NAME} .tar.gz ${FILE_NAME} RET_CD=$? if [ ${RET_CD} -eq 0 ];then rm -f ${FILE_DIR} /${FILE_NAME} fi done exit
処理イメージ
$ tree find_test1 find_test1 | aaa_20210320.tmp | aaa_20210321.tmp | aaa_20210322.tmp | bbb_20210323.tmp | bbb_20210324.tmp | bbb_20210325.tmp | ccc_20210326.tmp | ccc_20210327.tmp | ccc_20210328.tmp ∟ find_test2 | aaa_20210320.tmp | aaa_20210321.tmp | aaa_20210322.tmp | bbb_20210323.tmp | bbb_20210324.tmp | bbb_20210325.tmp | ccc_20210326.tmp | ccc_20210327.tmp | ccc_20210328.tmp ∟ find_test3 | aaa_20210320.tmp | aaa_20210321.tmp | aaa_20210322.tmp | bbb_20210323.tmp | bbb_20210324.tmp | bbb_20210325.tmp | ccc_20210326.tmp | ccc_20210327.tmp ∟ ccc_20210328.tmp $ ls -l ./find_test1 total 36 -rwxrwxr-x 1 finduser finduser 20 Mar 20 10:00 aaa_20210320.tmp -rwxrwxr-x 1 finduser finduser 20 Mar 21 10:00 aaa_20210321.tmp -rwxrwxr-x 1 finduser finduser 20 Mar 22 10:00 aaa_20210322.tmp -rwxrwxr-x 1 finduser finduser 20 Mar 23 10:00 bbb_20210323.tmp -rwxrwxr-x 1 finduser finduser 20 Mar 24 10:00 bbb_20210324.tmp -rwxrwxr-x 1 finduser finduser 20 Mar 25 10:00 bbb_20210325.tmp -rwxrwxr-x 1 finduser finduser 197 Mar 26 10:00 ccc_20210326.tmp -rwxrwxr-x 1 finduser finduser 197 Mar 27 10:00 ccc_20210327.tmp -rwxrwxr-x 1 finduser finduser 20 Mar 28 10:00 ccc_20210328.tmp $ cat ./find_test1/ccc_20210326.tmp aaa bbb ccc ddd eee $ bash ./find.sh $ $ tree find_test1 find_test1 | aaa_20210320.tmp | aaa_20210321.tmp | aaa_20210322.tmp | bbb_20210323.tmp | bbb_20210324.tmp | bbb_20210325.tmp | ccc_20210326.tmp.tar.gz | ccc_20210327.tmp.tar.gz | ccc_20210328.tmp ∟ find_test2 | aaa_20210320.tmp | aaa_20210321.tmp | aaa_20210322.tmp | bbb_20210323.tmp | bbb_20210324.tmp | bbb_20210325.tmp | ccc_20210326.tmp.tar.gz | ccc_20210327.tmp.tar.gz | ccc_20210328.tmp ∟ find_test3 | aaa_20210320.tmp | aaa_20210321.tmp | aaa_20210322.tmp | bbb_20210323.tmp | bbb_20210324.tmp | bbb_20210325.tmp | ccc_20210326.tmp | ccc_20210327.tmp ∟ ccc_20210328.tmp $ ls -l ./find_test1 total 36 -rwxrwxr-x 1 finduser finduser 20 Mar 20 10:00 aaa_20210320.tmp -rwxrwxr-x 1 finduser finduser 20 Mar 21 10:00 aaa_20210321.tmp -rwxrwxr-x 1 finduser finduser 20 Mar 22 10:00 aaa_20210322.tmp -rwxrwxr-x 1 finduser finduser 20 Mar 23 10:00 bbb_20210323.tmp -rwxrwxr-x 1 finduser finduser 20 Mar 24 10:00 bbb_20210324.tmp -rwxrwxr-x 1 finduser finduser 20 Mar 25 10:00 bbb_20210325.tmp -rw-rw-r-- 1 finduser finduser 197 Mar 29 12:27 ccc_20210326.tmp.tar.gz -rw-rw-r-- 1 finduser finduser 197 Mar 29 12:27 ccc_20210327.tmp.tar.gz -rwxrwxr-x 1 finduser finduser 20 Mar 28 10:00 ccc_20210328.tmp $ sudo tar xvzf ./find_test1/ccc_20210326.tmp.tar.gz ccc_20210326.tmp $ cat ./ccc_20210326.tmp aaa bbb find.shにより特定行を挿入 ccc ddd eee
想定通りの挙動をするシェルスクリプトになりました。
おわりに findは利用頻度は非常に高いのですが、-mtime
/-mmin
といった対象期間を限定する起点や終点についてよく迷ってしまうので、そんなときに本記事がお役に立てれば幸いかと思います。
また、今回紹介しきれませんでしたが、-atime
/-ctime
/-newer
など他にも様々なfind
オプションがありますので、もっともっとオプションを使いこなして素敵なfind
ライフを送りましょう。
シェルスクリプト連載 の3日目でした。明日は市川諒さんのdeclare使ってBashで配列と連想配列 です。