awk는 for 루프에서 파일 대신 파일 이름을 사용합니다.

awk는 for 루프에서 파일 대신 파일 이름을 사용합니다.

좋아, 그래서 awk를 사용하여 일부 파일에서 특정 열을 추출하고 이를 배열에 넣은 다음 정렬해야 합니다. 그런 다음 추출된 정렬 열에서 일부 값을 찾으려면 awk를 사용해야 하지만 이제 내 for 루프에 몇 가지 문제가 있습니다.

for var in $1 $2
do
myarr=($(awk -v row=$3 -F';' '$row!="" {print $row}' $var))
sorted_array=( $( printf "%s\n" "${myarr[@]}" | sort -n ) )
echo "${sorted_array[@]} $var"
done

출력은 다음과 같습니다

 dbdump.csv
 dbdump2.csv

이는 열을 추출하려는 두 csv 파일의 이름입니다. 콘텐츠를 검색하려면 이 스크립트가 필요하므로 누구든지 어떤 종류의 솔루션을 제공할 수 있다면 감사하겠습니다. 또한 더 빠른 알고리즘을 사용하는 방법을 제안할 수 있다면 그렇게 하십시오. 이것은 단지 제가 몇 가지 bash 스크립트를 배우고 몇 가지 코드를 작성하는 것입니다.

입력 파일에는 다음과 같은 레코드가 포함되어 있으며 그 중 두 개는 열 3에 일치하는 값이 없습니다(내 관리자가 말한 내용입니다).

1101590479;Frank Haemers;;20060310;1;RESI;;01;06;0007;0000000000;;CRM000;
1101590473;Van KetsmJan;;20060310;2;PROF;;01;08;;0000000000;75;CRM000;0686143950

이 두 파일에는 약 500만 개의 레코드가 포함되어 있습니다. 특정 수의 패턴이 포함된 또 다른 파일이 있고 이 두 개의 거대한 csv 파일을 찾아야 하며 패턴 중 하나가 두 파일에서 일치하면 다른 파일로 출력해야 합니다. 예를 들면 다음과 같습니다.

echo "$pattern has been found in $file"

패턴 텍스트 파일에 있는 모든 패턴에 대해 이 작업을 수행해야 합니다.

답변1

쉘 스크립트를 작성할 때 검증된 변수를 먼저 지정하고 파일 이름을 마지막에 지정하는 것이 좋습니다. 그러면 지정된 파일 수를 변경할 수 있습니다. 귀하의 경우에는 열 번호, 패턴이 포함된 파일 및 처리할 두 개(또는 그 이상)의 파일 이름이 있습니다. 이제 Bash 스크립트를 시작하세요

#!/bin/bash
if [ $# -lt 2 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
    echo ""
    echo "Usage: $0 [ -h | --help ]"
    echo "       $0 COLUMN PATTERNFILE [ FILE(s) ... ]"
    echo ""
    exit 0
fi

위 절은 if구식 POSIX 셸 형식을 사용하며 dash대부분의 구식 셸뿐만 아니라 다른 POSIX 셸 에서도 작동합니다 sh. 목적은 사용자가 명령줄 인수를 지정하지 않거나 -h또는 만 지정하는 경우 --help스크립트가 짧은 도움말 텍스트만 인쇄하는 것입니다.

그런데 도움말 텍스트를 확장해야 합니다. 작성한 내용을 잊어버리고 2~3개월 후에 도움말 텍스트의 목적을 이해하는 것이 더 쉽기 때문입니다. (이런 일은 나에게 항상 발생하며, 나는 그런 일을 겪었습니다.위치이와 같은 스크립트를 사용하므로 이 접근 방식은 약간의 노력을 기울일 가치가 있다고 생각합니다. )

다음으로, 필요한 인수(위의 하나만)를 추출하여 명령줄에 지정된 모든 파일 이름을 참조하는 shift데 사용할 수 있도록 꺼냅니다 ."$@"

column=$1
patternfile="$2"
shift 2

명시적으로 필요하지 않은 경우에도 쉘에서 확장하려는 항목 주위에 큰따옴표를 넣는 것을 좋아합니다. 제가 쉘 스크립트에서 겪는 실제 문제의 대부분은 다음과 같은 이유 때문입니다.잊다필요한 경우 참조 확장을 참조하세요. 이 방법은 기억하기 쉽고 "큰따옴표는 실제로 필요하지 않습니다"라는 짜증나는 콧소리를 제외하고는 아무런 해를 끼치지 않습니다.

awk그런 다음 이를 사용하여 입력 파일을 처리해 보겠습니다 .

awk -v column=$column \
  'BEGIN {
       RS = "[\t\v\f ]*(\r\n|\n\r|\r|\n|)[\t\v\f ]*"
       FS = "[\t\v\f ]*;[\t\v\f ]*"
   }

위의 첫 번째 줄 끝에 있는 백슬래시는 단순히 명령이 다음 줄에서 계속된다는 것을 쉘에 알려줍니다. 또한 종료하는 작은따옴표가 없으므로 '아래 줄은 실제로 우리가 제공한 명령줄 문자열 인수의 연속입니다 awk.

awk의 규칙은 BEGIN파일이 처리되기 전에 실행됩니다. 위의 내용은 RS레코드 구분 기호를 개행 규칙으로 설정하고 각 줄의 선행 또는 후행 공백을 포함합니다. 마찬가지로 필드 구분 기호는 세미콜론이지만 그 주위에 공백이 포함됩니다. 따라서 공백이 없는 첫 번째 필드 와 두 번째 필드 a ; b모두 두 개의 필드가 있습니다 .ab

어떤 입력 파일이 처리되고 있는지 추적하기 위해 다음 관용구를 사용합니다.

    FNR==1 { ++filenum }

단순히 우리가 처리하는 각 입력 파일의 첫 번째 레코드에 대해 변수를 증가시킨다는 의미라면 filenum. 초기화되지 않은 변수를 늘리는 것은 0을 늘리는 것과 같으므로 1첫 번째 입력 파일을 얻는 식입니다.

우리는 첫 번째 입력 파일(패턴 파일)의 각 줄 내용을 기억하고 싶습니다.

    filenum==1 { pattern[$0] }

awk 배열은 연관 배열이므로 알려진 패턴을 유지하기 위해 연관 배열을 사용할 수 있습니다. 위에서 우리는 흥미로운 awk 기능을 장점으로 사용했습니다. 아직 존재하지 않는 연관 배열 항목에 액세스하려고 하면 awk가 해당 항목을 생성합니다!

나머지 파일의 경우 필드 $column( awk 변수의 awk 스크립트릿에 제공됨 column)가 첫 번째 파일에 표시된 패턴과 (정확히) 일치하는지 확인하고, 그렇다면 전체 레코드를 인쇄합니다.

    filenum > 1 && ($column in pattern) { printf "%s\n", $0 }

$column위의 내용은 쉘 스크립트와 다른 의미를 갖습니다. 여기에는 column변수가 있으며 $column현재 레코드의 '번째 필드 값으로 확장됩니다(단, 0열은 전체 레코드입니다). column구문은 키가 포함되어 foo in array있는지 확인하는 데 사용되는 awkism입니다 . 따라서 요약하면 두 번째 및 추가 입력 파일의 경우 첫 번째 필드 값이 첫 번째 입력 파일에 나열되면 해당 레코드를 인쇄합니다. 표준 출력으로.arrayfoocolumn

우리는 여전히 명령줄 인수 문자열에 있으므로 awk작은따옴표 문자열을 닫아야 합니다. 또한 파일 이름을 지정하고 싶습니다.

    ' "$patternfile" "$@"

이 awk 스크립트가 완료되었습니다.

답변2

패턴 목록과 파일 세트를 가져오고 특정 열의 각 패턴과 일치하는 모든 파일 이름을 인쇄하려면 GNU awk(Linux의 기본값)만 있으면 됩니다.

awk -F';' '{
                if(NR==FNR){ 
                    p[$0]++; 
                    next
                } 
                if($3 in p){
                    printf "%s found in %s\n", $3,FILENAME; 
                    nextfile
                }
            }' patterns file1.csv file2.csv fileN.csv

설명하다

  • awk -F';': 필드 구분 기호를 로 설정합니다 ;.
  • if(NR==FNR){ p[$0]++;next}: NR현재 입력 줄 번호와 FNR현재 파일의 줄 번호입니다. 둘은 첫 번째 파일을 처리할 때만 동일합니다. 따라서 패턴 파일(첫 번째 파일)의 각 라인을 배열로 저장 p하고 해당 next라인으로 이동합니다. 패턴 파일에 대해서만 실행됩니다.
  • if($3 in p){printf "%s found in %s\n", $3,FILENAME; nextfile: 이제 csv 파일을 보고 있습니다. 세 번째 필드가 배열의 요소 중 하나인 경우 p(스키마 파일에 있는 경우) 세 번째 필드(스키마)와 해당 필드가 있는 파일 이름을 인쇄합니다. 그런 다음 다음 파일로 이동합니다. 이 FILENAME변수는 현재 처리 중인 파일의 경로를 보유합니다. 이것은 nextfile말한 대로 정확하게 수행하는 gawk 함수입니다. 처리할 다음 파일로 점프합니다.

예를 들어, 다음 파일이 제공됩니다.

$ cat patterns 
foo
bar
baz

$ cat file1.csv 
blah;blah;foo;blah
blah;blah;foo;blah
blah;blah;foo;blah

$ cat file2.csv 
blah;blah;bar;blah

$ cat file3.csv 
blah;blah;baz;blah

다음과 같은 결과가 출력됩니다.

$ awk -F';' '{if(NR==FNR){p[$0]++; next} if($3 in p){printf "%s found in %s\n", $3,FILENAME; nextfile}}' patterns file*csv 
foo found in file1.csv
bar found in file2.csv
baz found in file3.csv

각 패턴이 여러 파일에 존재할 수 있는 경우 약간 다른 접근 방식을 사용할 수 있습니다.

awk -F';' '{
            if(NR==FNR){ 
                p[$0]++; 
                next
            } 
            if($3 in p && !seen[FILENAME][$3]){
                printf "%s found in %s\n", $3,FILENAME; 
                seen[FILENAME][$3]++
            }
        }' patterns file1.csv file2.csv fileN.csv

이번에는 아닙니다. nextfile전체 파일을 처리하고 특정 파일에서 패턴이 발견될 때마다 카운터를 증가시켜야 하므로 동일한 패턴을 여러 번 보고하지 않습니다.

따라서 file1.csv위의 내용을 다음과 같이 변경하십시오.

$ cat file1.csv 
blah;blah;foo;blah
blah;blah;baz;blah
blah;blah;bar;blah
blah;blah;foo;blah

우리는 다음을 얻었습니다:

$ awk -F';' '{if(NR==FNR){p[$0]++; next} if($3 in p && !seen[FILENAME][$3]){printf "%s found in %s\n", $3,FILENAME; seen[FILENAME][$3]++}}' patterns file*csv 
foo found in file1.csv
baz found in file1.csv
bar found in file1.csv
bar found in file2.csv
baz found in file3.csv

속도가 너무 느린 경우(대용량 파일의 경우) 파일에서 모든 패턴이 발견되면 파일 읽기를 중지하도록 수정할 수 있습니다.

awk -F';' '{
            if(NR==FNR){ 
                p[$0]++; 
                next
            } 
            if($3 in p && !seen[FILENAME][$3]){
                printf "%s found in %s\n", $3,FILENAME; 
                seen[FILENAME][$3]++
            }
            if( length(seen[FILENAME]) == length(p) ){
                nextfile
            }
           }' patterns file1.csv file2.csv fileN.csv

관련 정보