forループからファイル名を読み取るには、findコマンドを実行するのに役立ちます。

forループからファイル名を読み取るには、findコマンドを実行するのに役立ちます。

コンテンツ/tmp/fefile

amx/eng/prf.amx
amx/eng/det.amx
bmb/menu.bmb
bmx/eng/menu.bmx
dll/tlnt.dll
dlx/eng/dlx
for file in `cat /tmp/fefile`
do
    if [ -f $file ]
    then
        echo "File '${file}' found in $(pwd) path."
        echo " Now i need to check if the  that file modified in last 10 mins with the below find command "
        find . -mmin -10 -type f -name ${file} -regextype posix-egrep -regex ".*/(dir1|dir1|dir3|dir4)/.+" -printf "%P\n" > /tmp/base
        echo "The files that are modified recently are below"
        File=(cat /tmp/base)

        echo " now i am verifying that $file is matched with $File "
        if[ $file == $File ]
        then
            echo " tmp file matched with base file."
        else
            echo " file doesn't match"
        fi # Originally missing
    else
        echo "File '{file} not found."
    fi
done

findコマンドで上記のスクリプトを変更してファイル名を読み、過去10分間変更されたことを確認します。

ファイルが変更されたら、2つのファイルが一致していることを確認してください。

答え1

内部に探す使用するコマンド正規表現。この正規表現は、リストされたファイル名を調べます。フェイペイファイルがありますが、この正規表現に一致するものはありません。注:ファイル内ではなくファイル名自体を確認してください。

find . -mmin -10 -type f -name ${file} -regextype posix-egrep -regex ".*/(dir1|dir1|dir3|dir4)/.+" -printf "%P\n" > /tmp/base

それらのどれもありません:

  • anything in any amount追加する
  • dir1またはdir1(再び?!おそらくdir2)または dir3またはdir4プラス
  • anything in any amount but at least one

もう一つの問題は正規表現そのものです。

".*/(dir1|dir1|dir3|dir4)/.+"

おそらく次のようになります。

".*\/(dir1|dir2|dir3|dir4)\/.+"

/次のようにエスケープを使用する必要があります\\/

返品:

File=(cat /tmp/base)

しなければならない:

File=$(cat /tmp/base)

または

File=`cat /tmp/base`

もう一つのポイントは行末ですfind

(...) -printf "%P\n" > /tmp/base

>を>>に変更することをお勧めします。

(...) -printf "%P\n" >> /tmp/base

それ以外の場合は、1つを除いて見つかったすべてのファイルを上書きします。

答え2

スクリプトスニペットの主な問題は、ループ内でfindを複数回実行することです(の各ファイル名に対して1回/tmp/fefile)。

これは非常に遅く非効率的ですfind「高価」他の選択肢がない限り、ループで繰り返し実行する必要がある作業(すべてのツールを使用してディレクトリツリーを繰り返すのは時間とディスクI / Oの面で高価です)よりも優れた選択肢がほとんどあります。

一度だけ実行して出力を処理することをお勧めしますfind(たとえば、grep、awk、sedなどを使用して)。

次のことをさらに試してください。

find ./dir[1234]/ -type f -mmin -10 -printf '%P\n' | grep -F -f /tmp/fefile

これにより、a)過去10分間修正され、b)に関連するdir1..dir4のすべてのファイルのリストが出力されます/tmp/fefile

ちなみに、ここでは一時/tmp/baseファイルは必要ありません。 (しかし、一時ファイル名をスクリプトとしてハードコーディングしたり、代わりmktempに使用したり、同様のものを使用したりするのは一般的に悪い考えです。ハードコーディングしないでください/tmp/fefile。この実行方法を知っておく必要があります。)

必要なものを得るには、少し調整やfindオプションgrepが必要な場合があります。必要な作業を把握するためにスクリプトの一部を確認するのに数分かかりましたが、それでも100%はわかりません。私はあなたが find 単独で、または find と grep (または sed、awk、perl などの他の一般的なツール) を使ってはるかに速く行うことができる非常に非効率的なタスクを実行するために約 20 行のシェル コードを使用します。あることを知っています。

注:ファイル名に改行文字が含まれていると正しく機能しません。\0GNU grepのオプションとともに、フォーマット文字列にnotを使用できます。\n-printf-z

find ./dir[1234]/ -type f -mmin -10 -printf '%P\0' | grep -z -F -f /tmp/fefile

(ターミナルで出力を表示するには、出力をtr '\0' '\n'. 、安全ではありません)

そして、ファイル名を扱う際の最良かつ安全な方法の1つは、ファイル名を配列に保存することです。たとえば、bash組み込みmapfile(AKA readarray)を使用します。プロセスの交換一致するすべてのファイル名で配列を入力します。

declare -a found
mapfile -d '' -t found < <(find ./dir[1234]/ -type f -mmin -10 -printf '%P\0' |
                             grep -z -F -f /tmp/fefile)

$found一致するすべてのファイル名を含む配列です。配列ビューを使用するdeclare -p found(デバッグ目的、配列に含める必要があると思うものが配列に含まれていることを確認するのに最も便利です)、コマンドの引数として使用するか、ループで使用できます。たとえば、次のようになります。

for f in "${found[@]}"; do
  echo "$f"
done

"$f"ループで何ができるのか、変数と配列にはNULを除くすべての文字を含めることができるので、二重引用符を使用する必要があります。

これはコマンド${file}で。代わりに使用していることを思い出させます。これは非常に一般的な間違いです。変数の周りの中かっこは次のとおりです。find"$file"いいえ引用された代替。

パラメータ置換(ヘッダの実行man bashと検索)に使用されます。Parameter Expansion文字列に変数名を挿入するときに変数名を明確にします。(たとえば、変数を呼び出し$fooて有効な変数名の文字の横にある文字列として印刷する必要がある場合は、echo "$food"$ food値を印刷し、echo "${foo}d"$ foo値を印刷してからリテラルd文字を表示します。)。

バラより$VAR対${VAR}と引用

また、見ることができますスペースやその他の特殊文字が原因でシェルスクリプトが停止するのはなぜですか?いつ二重引用符が必要ですか?そしてbash / POSIXシェルで変数を引用することを忘れてしまうセキュリティリスク

最後に、この質問はfind出力に関するものであり、検索に関する質問がいくつかありましたので、次を参照してください。検索結果を繰り返すのはなぜ悪い習慣ですか?。そしてリンクされた関連質問を必ず読んでください。

答え3

最後の 10 分間に最後に変更され、最後のパス コンポーネントが 1 行を構成する文字列であり、そのパスに , , 、 リストのディレクトリ コンポーネントが 1 つ以上含まれている一般的なファイルを探すことが/tmp/fefile焦点である場合は、次のようにします。できません。と一致し、完全に完了する必要があります。dir1dir2dir3dir4-name-path

-path(元はBSDから来たが今は標準)一部の実装では-wholename(と同じ-path)、、がフルパスと-ipath一致します。-regex-iregex

だからいくつかのオプションは

  • 各行に述語を使用するコマンドラインを生成しますfind-path/tmp/fefile

    LC_ALL=C find . '(' -path '*/dir1/*' -o \
                        -path '*/dir2/*' -o \
                        -path '*/dir3/*' -o \
                        -path '*/dir4/*' \
                    ')' '(' \
                        -path '*/amx/eng/prf.amx' -o \
                        -path '*/amx/eng/det.amx' -o \
                        ... \
                    ')' -type f -mmin -10
    

    Bashを使用すると、次のことができます。

    readarray -t args < <(
      </tmp/fefile LC_ALL=C sed '
    1!i\
    -o
    i\
    -path
    s|[*/?\\]|\\&|g; # escape glob operators
    s|.*|*/&|')
    LC_ALL=C find . '(' -path '*/dir1/*' -o \
                        -path '*/dir2/*' -o \
                        -path '*/dir3/*' -o \
                        -path '*/dir4/*' \
                    ')' '(' "${args[@]}" ')'
    

    (または*/dir[1234]/*これがパターンに簡単に分解できないいくつかの物理ディレクトリ名のプレースホルダであるとします。)

  • 後処理コマンドと一致するパスを残します。@casに見られるように

  • あるいは、findここで実装したのが述語をfindサポートするGNUであるようであるため、-regex正規表現を動的に構成します。

    regex=".*/($(
      </tmp/fefile LC_ALL=C sed -e 's/[][$^*()+{}\\|.?]/\\&/g' |
      paste -sd '|' -))\$"
    LC_ALL=C find . -regextype posix-extended \
                    -regex '.*/(dir1|dir2|dir3|dir4)/.*' \
                    -regex "$regex" \
                    -type f -mmin -10
    

    /tmp/fefile空ではないと仮定)。

-execこれらのファイルに対してコマンドを実行する必要がある場合は、いくつかの述語を追加するか、先行を削除して、-print0NUL区切りリストを処理できる他のコマンドにNUL区切りリストを渡します(パスから任意のファイルリストを渡す安全な方法)。 。-printf '%P\0'./

関連情報