タブで区切られた3つの列を含むテキストファイルがあり、3行目を1行ずつ読み、名前にこの名前を含むディレクトリ内のすべてのファイルを見つけます。最大1000個のエントリを含むファイルなので、検索で解決しようとすると時間がかかりすぎるため、適切ではありません。
while read f;
do var1=`echo "$f" | cut -f1`;
var2=`echo "$f" | cut -f2` ;
var3=`echo "$f" | cut -f3`;
echo "\n ID1 = $var1 \n ID2 = $var2 \n\n Path:";
find //myDirectory/ -type f -name *$var3* -not -path '*/zz_masters/*' -exec ls -Sd {} + ;
echo "\n----------------------";
done >> /SearchList.txt < /ResultList.txt
ご覧のとおり、一部のファイルの解像度が異なるため、1つのフォルダが除外され、結果がサイズでソートされました。
検索リスト.txt:
a1 a 1 x1 Trappist
b2 b 2 y2 Mars
c3 c 3 z3 Pegasi
結果:
/myDirectory/
ID1 = a1 a
ID2 = 1 x1
Path:
/myDirectory/xx/Trappist-1.png
/myDirectory/xx/Trappist-2.png
----------------------
ID1 = b2 b
ID2 = 2 y2
Path:
/myDirectory/yy/Mars-1.jpg
----------------------
ID1 = c3 c
ID2 = 3 z3
Path:
/myDirectory/xx/51PegasiB.tif
----------------------
私はより速く実行されることを願ってPerlを試してみました。私はPerlに最初に触れましたが、結果は不便でスクリプトに閉じ込められています。ループを生成します。私がいる場所は次のとおりです。
perl find.pl /myDirectory/ /SearchList.txt /ResultList.txt
#!/usr/bin/perl -w
use strict;
use warnings;
use File::Find;
open (IN, "$ARGV[1]") or die;
open(my $fh_out, '>', "$ARGV[2]");
my @files;
print $fh_out "$ARGV[0]\n";
while (my $line = <IN>) {
chomp $line;
my @columns = split(/\t/, $line);
find(sub {
push @files,"$File::Find::name" if /$columns[2]/;
### I think print has to be inside sub but each search result shows separately and is still slow:
# print $fh_out "\n\n----------------------------\n
#ID1: $columns[0]\nID2: $columns[1]Searchstring: $columns[2]\n
#Path:\n", "$File::Find::name\n" if /$columns[2]/;
}, $ARGV[0]);
### outside sub: displays the search results together, but also slow and with a loop :(
print $fh_out "\n\n----------------------------\n
ID1: $columns[0]\nID2: $columns[1]
Searchstring: $columns[2]\n\nPath:\n", join "\n", @files;
}
close IN;
close $fh_out;
exit;
Perlが私が望むスピードアップを提供できないのは可能ですか?そうでない場合、代替は何ですか?
答え1
Bashコードのコードレビュー:
read
あなたのために言葉を選ぶことができます。- echo "\n" は改行を印刷しません
$(...)
代わりに使用`...`
-引用する- 正しいインデントを使用して、リダイレクト記号にさらに注意してください。
while read -r var1 var2 var3 rest; do
printf "\n ID1 = %s \n ID2 = %s \n\n Path:\n" "$var1" "$var2"
find //myDirectory/ -type f -name "*$var3*" -not -path '*/zz_masters/*' -exec ls -Sd {} +
# ........................ quoted ^.......^
printf "\n----------------------\n";
done < /SearchList.txt > /ResultList.txt
しかし、作業速度を上げる方法はfind
一度だけ実行することです。
id1=()
id2=()
substrings=()
names=( -false )
declare -A paths=()
while read -r var1 var2 var3 rest; do
id1+=( "$var1" )
id2+=( "$var2" )
substrings+=( "*$var3*" )
names+=( -o -name "*$var3*" )
done < /SearchList.txt
find /myDirectory/ -type f \( "${names[@]}" \) -not -path '*/zz_masters/*' -prinf "%s %p\0" \
| sort -znr \
| while read -d '' -r size name; do
for s in "${substrings[@]}"; do
if [[ $name == *"$s"* ]]; then
paths[$s]+="$name"$'\n'
break
fi
done
done
fmt="\n ID1 = %s \n ID2 = %s \n\n Path:\n%s\n----------------------\n"
for idx in "${!id1[@]}"; do
printf "$fmt" "${id1[idx]}" "${id2[idx]}" "${paths[${substrings[idx]}]}"
done > /ResultList.txt
答え2
ファイル名にタブや改行が含まれていない場合は、次のことを試すことができます。
find . -type f -print |
awk '
NR==FNR {
name2ids[$3][1] = $1
name2ids[$3][2] = $2
next
}
{
for (name in name2ids) {
if ( index($NF,name) ) {
matches[name][$0]
}
}
}
END {
for (name in name2ids) {
print "ID1 =", name2ids[name][1]
print "ID2 =", name2ids[name][2]
print "\nPath:"
if (name in matches) {
for (file in matches[name]) {
print file
}
}
}
}
' FS='\t' SearchList.txt FS='/' -
上記はGNU awkを使用して配列の配列を処理します。以下はPOSIXバージョン(テストされていません)です。
find . -type f -print |
awk '
NR==FNR {
name2ids[$3] = $1 RS $2
next
}
{
for (name in name2ids) {
if ( index($NF,name) ) {
matches[name] = (name in matches ? matches[name] RS : "") $0
}
}
}
END {
for (name in name2ids) {
split(name2ids[name],ids,RS)
print "ID1 =", ids[1]
print "ID2 =", ids[2]
print "\nPath:"
split(matches[name],files,RS)
for (idx in files) {
print files[idx]
}
}
}
' FS='\t' SearchList.txt FS='/' -