스크립트를 작성해야 합니다.
- 여러 텍스트 파일이 포함된 디렉터리를 가져옵니다. 여러 개일 수 있으며 최대 1000개까지 가능합니다.
- 모든 파일은 주어진 줄(항상 같은 줄)에 식별자를 포함합니다.
- 고유하지 않은 식별자를 가진 파일, 즉 디렉터리의 다른 파일에 중복된 파일을 식별합니다.
- 중복 목록 내보내기 또는 저장
이는 고유해야 하지만 사용자 오류로 인해 고유하지 않을 수 있는 시스템 생성 파일의 일상적인 관리 "정리"에 필요합니다.
답변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
요청하기 때문에 다음으로 시작하는 줄과 나머지 줄이 포함되어 있습니다.-n
grep
[filename]:[matching line number]:[IDmatch]
sed
이전 버퍼에 줄의 복사본을 저장하기 위해 이를 전달한 h
다음 문자열을 확인 :6:ID
하고 발견되면 해당 줄을 삭제합니다 ID
. 그런 다음 p
결과를 인쇄합니다.
다음으로, g
버퍼를 반환하고(프로세스의 마지막 편집 내용을 덮어쓰며) grep
일치하는 파일 이름 위치로 일치 라인을 바꿉니다. 따라서 grep
6행과 일치하는 인쇄된 모든 행을 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
...
그런데 그 해결책~ 할 것이다파일 이름에 \n
ewline 문자가 포함되어 있으면 중단되지만 다음은 그렇지 않습니다. 두 번 접지할 필요가 없도록 다음을 쉘 함수에 넣는 방법을 알아냈습니다. 곧 여기에 붙여넣겠습니다.
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
했습니다 .stderr
stdout
이상한 파일 이름이 결과를 망치는 것에 대해 걱정하는 것보다 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바이트는 생성되지 않는지 확인하고 싶지 않아서 file89
90으로 반올림해서 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 -u
comm
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]
).awk
gawk
답변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
-l
print
-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