すべてのサブディレクトリを再帰的に移動し、特定の拡張子を持つファイルがある場合は、そのフォルダでコマンドを一度実行します。

すべてのサブディレクトリを再帰的に移動し、特定の拡張子を持つファイルがある場合は、そのフォルダでコマンドを一度実行します。

フォルダ内のすべてのサブディレクトリを再帰的に参照する必要があります。サブディレクトリに拡張子が ".xyz"のファイルがある場合は、そのフォルダで特定のコマンドを一度実行する必要があります。

これが私が今まで持っているものです

recursive() {
  for d in *; do
    if [ -d "$d" ]; then
      (cd -- "$d" && recursive)
    fi
  dir=`pwd`   
  pattern="*.xyz"
file_count=$(find $dir -name $pattern | wc -l)
if [[ $file_count -gt 0 ]]; then
    echo "Match found. Going to execute a command"
    #execute command
fi
  done
}

(cd /target; recursive)

ところで、問題は、一致するものがあるときに「一致項目が見つかりました」というメッセージが各フォルダに複数回表示されることです。この問題を解決しながら、より簡単な方法はありますか?

答え1

あなたは再創造していますfind

次のようにしてみてください(GNUfindutilsとGNUを使用sort)。

find /target -iname '*.xyz' -printf '%h\000' | sort -z -u | 
  xargs -0 -r -I {} sh -c "cd {} ; yourcommandhere"

「*.xyz」ファイルのあるディレクトリ名()を-printfNULバイト()で区切って印刷します。重複を削除し、各ディレクトリに移動して実行します。%h\000sortxargscdyourcommandhere

xargs を使用して実行するスクリプトを作成することもできます。例えば

find /target -iname '*.xyz' -printf '%h\000' | sort -z -u | 
  xargs -0 -r /path/to/myscript.sh

単純なmyscript.shの例:

#!/bin/sh

for d in "$@" ; do
  cd "$d"
  echo "Match found in $d. Going to execute command"
  # execute command
done

一致するディレクトリが多い場合、2番目のバージョンははるかに高速です。つまり、ディレクトリごとにシェルをフォークするのではなく、シェルを一度だけフォークしてから、各引数に対して繰り返します。


ところで、ここではどちらも実際にはprintf必要ありません。しかし、何が起こっているのかを読んで理解しやすくなります。同様に重要なのは、(printfとsortを使用して)最初に重複するエントリを削除することでbashを使用するよりもはるかに速く実行され、特定のディレクトリでコマンドを複数回実行する危険(やや小さい)を排除することです。sortxargs

ソートや xargs なしで同じ操作を実行する別の方法は次のとおりです。

find /target -iname '*.xyz' -exec bash -c \
    'typeset -A seen
     for f in "$@"; do
       d="$(dirname "$f")";
       if [[ ! -v $seen[$d] ]]; then
         echo "Match found in $d. Going to execute command"
         # Execute command
         seen["$d"]=1
       fi
     done' {} +

これはbash()の連想配列を使用して、$seen[]どのディレクトリが検索され処理されたかを追跡します。何千もの一致するファイルがある場合*.xml(bashスクリプトが複数回フォークできるように最大コマンドライン長を超えるのに十分です)、次のコマンドを実行します。可能特定のディレクトリで複数回実行します。

findオプションで実行されるスクリプトは、上記-execのxargsバージョンなどのスタンドアロンスクリプトです。

しかし、ここのすべてのバリアントは、awkやperl、shやbashスクリプトではなく、すべてのスクリプトを簡単に実行できます。

答え2

find文字列を印刷する組み込みフラグがあります。これは非常に便利です。

find -iname "*.xyz" -printf "%h\n"パターンに一致するファイルを含むすべてのディレクトリの名前を印刷します(の魔法の構文は%hもちろん、改行文字を使用してfindファイルディレクトリに展開されます\n)。

だからこれがあなたが望むものです:

COMMAND='echo'
find `pwd` -iname "*.pdf" -printf "%h\n" | sort -u | while read i; do                                              
    cd "$i" && pwd && $COMMAND
done

ここで何が起こっています。コマンドを一度だけ実行するには、重複したエントリを削除するsortフラグを使用してパイプします。-uその後、すべてを繰り返しますwhile。また、相対パスではなく絶対パスを出力するfind `pwd`ために良いトリックインを使用したことに注意してください。これにより、相対パスを気にすることなくfind使用できます。cd

編集:このスクリプトを実行するときは、ディレクトリ名に注意してください。改行(\n)を含むディレクトリ名\はスクリプトを破る可能性があります(そしてあまり一般的ではないかもしれませんが、まだテストされていません)。この問題を解決するのは難しく、どうすればいいかわからないので、そのようなディレクトリを使用しないようにアドバイスするしかありません。

関連情報