파일에서 동일한 텍스트 항목을 검색하는 쉘 스크립트

파일에서 동일한 텍스트 항목을 검색하는 쉘 스크립트

스크립트를 작성해야 합니다.

  1. 여러 텍스트 파일이 포함된 디렉터리를 가져옵니다. 여러 개일 수 있으며 최대 1000개까지 가능합니다.
  2. 모든 파일은 주어진 줄(항상 같은 줄)에 식별자를 포함합니다.
  3. 고유하지 않은 식별자를 가진 파일, 즉 디렉터리의 다른 파일에 중복된 파일을 식별합니다.
  4. 중복 목록 내보내기 또는 저장

이는 고유해야 하지만 사용자 오류로 인해 고유하지 않을 수 있는 시스템 생성 파일의 일상적인 관리 "정리"에 필요합니다.

답변1

위의 의견을 토대로 내 테스트 데이터가 실제 데이터와 매우 유사하다는 점을 확인하여 다음을 확인할 수 있었습니다.

grep -n '^ID.[^:-]*.[0-9][0-9]*$' |
sed -n 'h;s|\(.*\):6:\(ID.*\)|\2|p;g;s||\2:\1|p'
sort -u | 
sed 's|ID..*:||'

grep폴더에는 일치하는 파일이 여러 개 발견되어 일치하는 줄 번호를 인쇄하도록 ID요청하기 때문에 다음으로 시작하는 줄과 나머지 줄이 포함되어 있습니다.-ngrep

[filename]:[matching line number]:[IDmatch]

sed이전 버퍼에 줄의 복사본을 저장하기 위해 이를 전달한 h다음 문자열을 확인 :6:ID하고 발견되면 해당 줄을 삭제합니다 ID. 그런 다음 p결과를 인쇄합니다.

다음으로, g버퍼를 반환하고(프로세스의 마지막 편집 내용을 덮어쓰며) grep일치하는 파일 이름 위치로 일치 라인을 바꿉니다. 따라서 grep6행과 일치하는 인쇄된 모든 행을 sed다음으로 바꾸십시오.

[IDmatch]
[IDmatch]:[filename]

이 데이터가 전달되면 sort전체 컬렉션을 구성 ID하고 고유한 결과만 제공하도록 요청하므로 -u중복 IDmatch행을 제외한 모든 행을 제거하고 다음 IDmatch:filename행은 유지합니다. 다음 sed명령문은 이를 정리하여 다음과 같이 렌더링합니다.

ID00000000
ID00000000:file00
ID00000000:file10
...
ID00000000:file80
ID00000001
ID00000001:file01
ID00000002
ID00000002:file02
...

이와 같이:

ID00000000
file00
file10
...
file80
ID00000001
file01
ID00000002
file02
...

그런데 그 해결책~ 할 것이다파일 이름에 \newline 문자가 포함되어 있으면 중단되지만 다음은 그렇지 않습니다. 두 번 접지할 필요가 없도록 다음을 쉘 함수에 넣는 방법을 알아냈습니다. 곧 여기에 붙여넣겠습니다.

for f in * ; do
    sed '5!d;s|^|: "${'$((i=i+1))'}" |;q' "$f"
done |
sort -t' ' -k3 |
uniq -D -f2 |
sh -cx "$(cat)" -- * 2>&1

이는 가능해야 합니다. 명령문에 있는 ID 줄을 로 5바꾸면 됩니다 . sed제 생각에는 – 제가 틀렸다면 알려주세요 – 이것이 모든 경우를 처리합니다.

디렉토리의 각 파일에 대해 숫자를 1씩 증가시키고 문자열로 시작하는 줄을 인쇄합니다...

: "${[num]}" ...

...여기서 [num]방금 1만큼 증가한 실제 정수이고 ...행의 고유 ID입니다.

그런 다음 해당 행을 먼저 파이프하여 sort문자 <space>를 구분 기호로 처리하고 세 번째 필드의 데이터만 정렬합니다. |pipeline옆에 있는 계속은 입력을 비교하고 중복 행만 인쇄하는 동안 입력의 처음 두 필드를 분리하고 건너뜁니다 uniq. 다음 부분은 좀 이상합니다.<space>-D

따라서 다시 반복하여 어떤 파일이 무엇인지 알아내는 대신 [num]앞서 언급한 작업을 수행했습니다. sh최종 셸 프로세스가 결과를 통과 하면 |pipeline이 숫자만 받습니다. 그러나 이미 해당 숫자를 증가시킬 때 반복한 것과 동일한 전역 변수로 설정된 위치 매개변수가 있으므로 해당 숫자를 평가할 때 이미 위치 배열에 있는 파일과 연결합니다. 그게 전부입니다.

사실 – 거의 그렇게 하지 않습니다. 각 위치 매개변수 앞에는 :널 명령이 옵니다. 셸 프로세스가 수행하는 유일한 작업은 전달된 변수를 평가하는 것입니다. 단 한 줄의 코드도 실행하지 않습니다. 하지만 디버그 모드로 설정 하고 모든 파일 이름을 인쇄하도록 리디렉션 -x했습니다 .stderrstdout

이상한 파일 이름이 결과를 망치는 것에 대해 걱정하는 것보다 sort | uniq훨씬 쉽기 때문에 이렇게 합니다. 그리고 그것은 훌륭하게 작동합니다.

다음과 같은 방식으로 생성된 데이터 세트를 사용하여 이를 테스트했습니다.

tr -dc '[:graph:]' </dev/urandom |
dd ibs=100 cbs=10 conv=unblock count=91 |
split -b110 --filter='
{   c=${FILE##%%*0} ; c=${c#file}
    sed "5cID000000${c:-00}"
} >$FILE' -ed - file ; rm *90*

rm위의 문자열을 참고하세요 . 좀 졸려서 왜 102바이트만 생성되고 나머지 110바이트는 생성되지 않는지 확인하고 싶지 않아서 file8990으로 반올림해서 rm편집했습니다. 위 명령을 실행하면 rm 파일 이름이 현재 디렉터리의 glob과 일치하고 file00그 안의 모든 파일을 덮어쓰게 됩니다. file89하지만 위임된 테스트 디렉터리 내에서 사용하면 완전히 안전합니다.

...무엇보다도...누구에게나 효과가 있습니다.

file[0-8][1-9]이렇게 하면 각각 1-4,6-10 10바이트 임의의 데이터 줄로 명명된 90개의 파일이 작성되고 각 파일의 줄 5에 고유 ID가 있습니다. 또한 file[0-8]0항상 행 5를 생성합니다 ID00000000.

이 데이터 세트에서 실행되는 최상위 작은 함수의 출력은 다음과 같습니다.

+ : file10 ID00000000
+ : file00 ID00000000
+ : file20 ID00000000
+ : file30 ID00000000
+ : file40 ID00000000
+ : file50 ID00000000
+ : file60 ID00000000
+ : file70 ID00000000
+ : file80 ID00000000

+어떤 이유로 출력의 기호가 마음에 들지 않으면 $PS4마지막 쉘 프로세스를 변경하십시오. 마지막 줄의 시작 부분에 다음을 추가하면 이 문제를 처리할 수 있습니다.

PS4= sh ...

그러나 원하는 경우 쉘 스크립트의 실행 가능 비트까지 임의의 문자열로 설정할 수도 있으며 원하는 대로 파일 이름을 구분합니다. 기본적으로 힌트를 자동 구분 기호로 자유롭게 사용할 수 있습니다. 마지막 셸 프로세스에는 여전히 배열의 파일 이름이 포함되어 있습니다. 원하는 경우 데이터를 조작하는 명령을 추가할 수 있습니다.

답변2

파일 이름에 공백이나 개행 문자가 없고 uniq이 옵션을 지원하는 GNU를 사용할 수 있다고 가정하면 이는 매우 간단합니다( 식별자의 줄을 변경하려면 -D다음 숫자를 변경하십시오 ).FNR==

awk 'FNR==2 { print FILENAME,$0 }' * | sort -k 2 | uniq -Df 1 | cut -d ' ' -f 1

-D옵션이 없으면 상황이 빠르게 더 복잡해질 수 있습니다. 이를 수행하는 한 가지 방법은 uniq다음을 사용하여 결과를 반대로 하는 것입니다.uniq -ucomm

awk 'FNR==2 { print FILENAME,$0 }' * | sort >/tmp/sorted_keys
sort -k 2 /tmp/sorted_keys |
  uniq -uf 1 | sort | comm -23 /tmp/sorted_keys - | cut -d ' ' -f 1

임의의 이름을 가진 파일에 대해 이 작업을 수행하려면 아마도 가장 좋은 방법은 다음과 같습니다( 식별자 줄을 변경하려면 perl줄 1 다음의 숫자를 변경하세요 ).$.==

perl -ne 'push(@{$table{$_}}, $ARGV) if $.==2;
  $.=0 if eof;
  END {
    for my $val (values %table) {
      print join( "\n", @{$val} ) . "\n" if @{$val} > 1;
    }
  }' *

아이디어는 파일에서 발견된 식별자로 각 파일 이름을 색인화하여 각 식별자를 사용하여 파일 이름 배열을 얻을 수 있도록 하는 것입니다. 이렇게 하면 여러 요소가 포함된 각 배열을 쉽게 인쇄할 수 있습니다.

고쳐 쓰다

실제로 위와 동일한 접근 방식을 사용할 수 있습니다 awk.

awk 'FNR==2 {
  i=table_sizes[$0]++;
  table[$0,i]=FILENAME
  }
  END {
    for (key in table_sizes) {
      if (table_sizes[key] > 1) {
        for (long_key in table) {
          if ( index(long_key, key SUBSEP) == 1 ) {
            print table[long_key]
            delete table[long_key]  # speed up next search
          }
        }
      }
    }
  }' *

유일한 질문은 값이 SUBSEP식별자에 나타나는지 여부입니다. 일반적으로 SUBSEP인쇄되지 않는 문자( 0x1c)이므로 대부분의 텍스트 파일에서는 문제가 되지 않습니다. 필요한 경우 변경될 수 있으며, 이를 지원하는 실제 다차원 배열에 예제를 적용할 수 있습니다(예: array[x][y]대신 array[x,y]).awkgawk

답변3

형식을 설명하면 더 구체적인 정보를 제공할 수 있지만 논의를 위해 식별자가 각 파일의 세 번째 줄에 있는 공백으로 구분된 첫 번째 단어라고 가정하겠습니다. 그렇다면 다음과 같이 할 수 있습니다.

for f in *; do printf "%s\t%s\n" "$f" $(awk 'NR==3{print $1}' "$f"); done |
 perl -F"\t" -lane '$k{$F[1]}{$F[0]}++; 
  END{
   foreach (keys(%k)){
     print "$_ : ", join ",",keys(%{$k{$_}}) if scalar (keys(%{$k{$_}})) > 0 }
  }'

설명하다

  • for f in *; do printf "%s\t%s\n" "$f" $(awk 'NR==3{print $1}' "$f"); done: 현재 디렉터리(및 하위 디렉터리)의 모든 파일을 반복하고 파일 이름, 탭 문자( \t) 및 세 번째 줄의 첫 번째 필드( 명령 awk)를 인쇄합니다.

  • perl -F"\t" -lane: 이 -a플래그는 입력 라인을 주어진 문자를 기반으로 필드로 자동 분할 하고 해당 필드를 배열 에 저장하는 것과 perl같은 작업을 수행합니다 . 각 입력 줄에서 후행 줄 바꿈을 제거 하고 실행해야 하는 스크립트인 각 호출에 줄 바꿈을 추가합니다 .awk-F@F-lprint-e

  • $k{$F[1]}{$F[0]}++: 해시 해시에 파일 이름/식별자 쌍을 저장합니다. 여기서 식별자는 첫 번째 해시의 키이고 파일 이름은 두 번째 해시의 키입니다. 결과 구조는 다음과 같습니다.

    $k{identifier1}{filename1}
    $k{identifier1}{filename2}
    $k{identifier1}{filenameN}
    
  • END{}블록은 전체 입력을 읽은 후 실행됩니다.

  • foreach루프는 해시 %k(파일 이름)의 각 키를 반복하고 식별자( $_, 키)와 하위 해시( )의 키 목록을 인쇄합니다 keys(%{$k{$_}}.

다음 명령으로 생성된 파일 세트를 테스트했습니다.

for i in {1..5}; do echo -e "$RANDOM\nbar\n$i" | tee file$i > file${i}d; done

위 코드는 동일한 세 번째 줄을 사용하여 5쌍의 파일(file1/file1d ~ file5/file5d)을 생성합니다. 이러한 파일에 대해 위 명령을 실행하면 다음이 생성됩니다.

id2 : file2d,file2
id4 : file4,file4d
id5 : file5d,file5
id1 : file1,file1d
id3 : file3,file3d

관련 정보