我正在使用下面的 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 中进行比较以查找是否有任何匹配项可用。如果可用,它将把整个路径放入文件中。如果不可用,则会将其放入不同的文件中。然而,此脚本需要 2 天才能读取 8000 条记录。有没有办法优化它?
答案1
答案2
与xargs
+find
一种解决方案是使用xargs
构建非常长的find
命令来一次搜索数千个文件:
sed -e 's/^/-o -name /' "${Region}_${date}.txt" \
| xargs find "$DataDir" -false \
> "${runDir}/st_$Region"
第一个命令将每个文件名转换为将附加到该命令sed
的表达式。然后执行它构建的命令。结果直接存储到文件中。-o -name filename
xargs
find
xargs
find
st_$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 -3
抑制两个文件之间的公共行。这些实际上是伪文件。第二个文件是basename
对找到的每个文件应用命令的结果。两个文件均已排序。
与find
+grep
另一个解决方案是grep
从find
.提供了(通过选项)搜索文件中存储的一系列模式的grep
可能性。-f
我们在一个文件中有一系列文件名。让我们将其设为模式列表并将其提供给grep
:
find "$DataDir" \
| grep -f <(sed 's|.*|/&$|' "${Region}_${date}.txt") \
> "${runDir}/st_$Region"
该sed
命令是强制性的:它将要搜索的文件名锚定在路径末尾。
至于丢失文件列表,它将以与其他解决方案相同的方式构建。
这个解决方案的问题是文件名可能包含可以被grep
: .
、*
、[
等解释的字符。我们必须用sed
(我把它作为练习留给读者)来转义它们。这就是为什么第一个解决方案是首选的恕我直言。
最后,请注意,我在这里使用了一些bash
主义(例如过程替换<(...)
)。不要指望我的任何解决方案都符合 POSIX 标准。
答案3
该脚本仅适用于特定文件的 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"