xargs+へfind

xargs+へfind

以下のwhileループを使用してファイルを読み込みます。

while read file
do
    FileFound="`find $DataDir -name $file -print 2>/dev/null`"
    if [ -n "$FileFound" ]; then
        echo $FileFound >> ${runDir}/st_$Region
        else
            echo $file >> ${APP_HOME}/${Region}_filesnotfound_$date.txt
        fi
done<${Region}_${date}.txt

whileループはファイル名を読み取り、datadirでそれらを比較して一致するものがあるかどうかを確認します。利用可能な場合は、フルパスをファイルに入れます。利用できない場合は、別のファイルに保存されます。ただし、このスクリプトは8000レコードを読み取るのに2日かかります。最適化する方法はありますか?

答え1

最新のLinuxデスクトップを使用している場合は、次のファイルインデックス作成ツールがあります。mlocateバックグラウンドでファイルがすでにインストールされており、インデックス化されています。その場合は、次のものを使用できます。

while read file
do
    locate "$file" >> "${runDir}/st_$Region" || echo "$file" >> "${APP_HOME}/${Region}_filesnotfound_$date.txt"
done<"${Region}_${date}.txt"

探しているファイルが頻繁に更新される場合は、まずデータベース更新を手動で強制することができます。updatedbまたはあなたのバージョンに合うものは何でも可能ですlocate

答え2

xargs+へfind

xargs1つの解決策は、非常に長いコマンドを使用してfind何千ものファイルを一度に検索することです。

sed -e 's/^/-o -name /' "${Region}_${date}.txt" \
| xargs find "$DataDir" -false \
> "${runDir}/st_$Region"

最初のコマンドは、各ファイル名をsedコマンドに追加される式に変換します。次に、ビルドされたコマンドを実行します。結果はファイルに直接保存されます。-o -name filenamexargsfindxargsfindst_$Region

美しい。しかし、${Region}_filesnotfound_$date.txt見つからないファイルのリストをどのように作成しますか?見つかったファイルのリストと元のリスト全体を交差させるだけです。

comm -3 \
    <(sort -u "${Region}_${date}.txt") \
    <(xargs -L1 basename < "${runDir}/st_$Region" | sort -u) \
    > "${Region}_filesnotfound_$date.txt"

comm -32つのファイル間の共通行を抑制します。これは実際に偽のファイルです。 2番目のファイルは、basename見つかった各ファイルにコマンドを適用した結果です。両方のファイルがソートされました。

find+へgrep

別の解決策は、(オプションgrepfind)ファイルに保存されている一連のパターンを検索する可能性を提供することですgrep-fファイルには一連のファイル名があります。これをパターンリストにして、次に供給してみましょうgrep

find "$DataDir" \
| grep -f <(sed 's|.*|/&$|' "${Region}_${date}.txt") \
> "${runDir}/st_$Region"

このsedコマンドは必須です。検索するファイル名をパスの末尾に固定します。

欠落しているファイルのリストは、他のソリューションと同じ方法で構築されます。

この回避策の問題は、ファイル名にgrep.、などとして解釈できる文字を含めることができることです*[私たちはこれを使ってそれらを脱出しなければなりませんsed(私はこれを読者に練習として残します)。これが最初のソリューションがIMHOを好む理由です。

最後に、ここではいくつかの注意bash(例:手続き型置換<(...))を使用しています。私のソリューションがPOSIXと互換性があるとは思わないでください。

答え3

このスクリプトは、特定のファイルが1回発生した場合にのみ機能します。したがって、異なるディレクトリに同じ名前の2つのファイルがある場合は、1つだけが報告されます。まだテストされていません。

declare -a arr
tmp1=$$tmp1

while read file
do
    base=$(basename "$file")
    echo "$base" >> "$tmp1"
    arr["$base"]="$file"
done <(find "$DataDir")

cat "$tmp1" | sort | uniq > "$tmp1"
tmp2=$$tmp2
cat "${Region}_${date}.txt" | sort | uniq > "$tmp2"

for file in "$(join <(cat "$tmp1") <(cat "$tmp2"))"
do
    echo "${arr["$file"]}" >> ${runDir}/st_$Region
done

for file in "$(cat "$tmp1" "$tmp2" | sort | uniq -u)"
do
    echo "$file" >> ${APP_HOME}/${Region}_filesnotfound_$date.txt
done

rm "$tmp1"
rm "$tmp2"

答え4

このスクリプトの遅い部分は、findファイル全体$DataDirで一致するものを検索することです。このコンポーネントの大部分をループの外に移動すると、時間を節約できます。

ftmp=$(mktemp -t)
find "$DataDir" >"$ftmp" 2>/dev/null

while IFS= read -r file
do
    if grep -Fx -q "$file" "$ftmp"    # No RE patterns. Match full line
    then
        echo "$file" >>"$runDir/st_$Region"
    else
        echo "$file" >>"${APP_HOME}/${Region}_filesnotfound_$date.txt"
    fi
done <"${Region}_${date}.txt"

rm -f "$ftmp"

ファイルのリストが${Region}_${date}.txt非常に大きい場合は、ファイル全体を渡し、リスト全体と一致grepセットcomm内の一致しない項目を識別して追加で保存できます。ここでの欠点は、commリストをソートする必要があるため、出力結果のリストもソートされることです。

fdata=$(mktemp -t)
fmatch=$(mktemp -t)
find "$DataDir" >"$fdata" 2>/dev/null

# No RE patterns. Match full line
grep -Fx -f "${Region}_${date}.txt" "$fdata" |
    tee -a "$runDir/st_$Region" |
    sort >"$fmatch"

# Pick out the filenames that didn't match
sort "${Region}_${date}.txt" |
    comm -23 - "$fmatch" >>"${APP_HOME}/${Region}_filesnotfound_$date.txt"

rm -f "$fdata" "$fmatch"

関連情報