읽는 동안 줄 찾기 - Perl을 사용하면 더 빨라지나요?

읽는 동안 줄 찾기 - Perl을 사용하면 더 빨라지나요?

탭으로 구분된 세 개의 열이 포함된 텍스트 파일이 있고 세 번째 열을 한 줄씩 읽어서 이름에 이 이름이 있는 디렉터리의 모든 파일을 찾습니다. 최대 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

보시다시피 일부 파일의 해상도가 다르기 때문에 한 폴더가 제외되었으며 결과가 크기별로 정렬되었습니다.

검색 목록.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='/' -

관련 정보