HFS+ 파일 시스템에서 많은 수의 파일을 전송하고 있습니다.
이 파일은 현재 ext2 파티션에 있습니다.
대상 파티션(HFS+)이 대소문자를 구분하지 않기 때문에 충돌이 발생합니다.
중복된 파일 이름을 가진 소문자 파일을 식별하고 실제로 중복된 경우 제거하고 싶습니다.
또한 모든 것을 소문자로 변환하면 폴더 이름이 중복된다는 사실도 발견했습니다. 기본적으로 이러한 하드 드라이브에는 수년간 정렬되지 않은 데이터가 포함되어 있으며 폴더 이름에도 문제가 있습니다.
이것이 합리적으로 보입니까?
find . -type f | while read f; do echo $f:l; done | sort | uniq -d
$f:l
소문자로 변환하는 것은 ZSH입니다.
이제 중복된 각 파일의 인스턴스 하나만 유지하고 싶습니다. 이를 효과적으로 수행하는 방법은 무엇입니까?
중복된 파일을 찾고 싶지 않습니다.콘텐츠, 동일한 소문자 파일 이름을 갖지 않는 한. 나중에 중복된 내용을 다루겠습니다.
답변1
파이프라인의 두 번째 단계는 약간 손상되었으며(백슬래시와 선행 및 후행 공백 분리) 복잡한 접근 방식입니다. tr
소문자로 변환하는 데 사용됩니다 . 검색을 파일로 제한해서는 안 됩니다. 디렉터리에서도 충돌이 발생할 수 있습니다.
find . | tr '[:upper:]' '[:lower:]' | LC_ALL=C sort | LC_ALL=C uniq -d
이는 파일 이름에 개행 문자가 포함되지 않은 경우에만 작동합니다. Linux에서는 줄바꿈을 처리하기 위한 구분 기호로 널 바이트로 전환합니다.
find . -print0 | tr '[:upper:]' '[:lower:]' | LC_ALL=C sort -z | LC_ALL=C uniq -dz
이는 파일 이름의 소문자 버전을 인쇄하는데, 이는 실제로 파일에 대한 특정 작업을 수행하는 데 도움이 되지 않습니다.
zsh를 사용한다면 잊어버리십시오 find
. 필요한 모든 것이 zsh에 내장되어 있습니다.
setopt extended_glob
for x in **/*; do
conflicts=($x:h/(#i)$x:t)
if (($#conflicts > 1)); then
## Are all the files identical regular files?
h=()
for c in $conflicts; do
if [[ -f $c ]]; then
h+=(${$(md5sum <$c)%% *})
else
h=(not regular)
break
fi
done
if (( ${#${(@u)h}} == 1 )); then
# Identical regular files, keep only one
rm -- ${conflicts[1,-2]}
else
echo >&2 "Conflicting files:"
printf >&2 ' %s\n' $conflicts
fi
fi
done
답변2
중복된 파일 이름에 대해서만 awk를 사용하여 솔루션을 개발 중이므로 내용을 비교하지 않습니다.
이것은 awk 파일입니다dups.awk
#!/usr/bin/awk -f
{
lc=tolower($0);
count[lc] = count[lc]+1;
tab[lc] = tab[lc] "*" $0;}
END {for (t in tab)
if (count[t]>1) {
split(tab[t],sp,"*");
r=1;sep="# ";
for (fn in sp)
if (length(sp[fn]))
{
print sep "rm '" sp[fn] "'";
if (r==1) {r=0; sep=" ";}
}
print ""; }
}
나는 그것을 이렇게 부른다:
#!/bin/zsh
find $1 -type f | dups.awk
한 가지 결함이 있습니다. 별표가 있는 파일 이름에서는 작동하지 않습니다.
여기에서 조치를 취하세요.
ks% md5sum test/*
e342e6ab6ae71954a772409f23390fa4 test/file1
e342e6ab6ae71954a772409f23390fa4 test/File1
e342e6ab6ae71954a772409f23390fa4 test/file2
ks% ./dupsAwk.sh test
# rm "test/File1"
rm "test/file1"
답변3
File::Find
쉘의 복잡성을 해결하는 대신 Perl을 사용하는 솔루션은 다음과 같습니다 .
#!/usr/bin/env perl
use strict;
use warnings;
use File::Find;
use Digest::MD5 qw(md5); # To find duplicates
my %lower_case_files_found;
find(
sub{
-f or return; # Skip non-files
push @{$lower_case_files_found{+lc}},$File::Find::name;
},
'.'
);
for my $lower_case_name (sort keys %lower_case_files_found){
my $number_of_files = scalar @{$lower_case_files_found{$lower_case_name}};
if($number_of_files > 1){
my %digests_seen;
for my $file (@{$lower_case_files_found{$lower_case_name}}){
open my $fh,'<',$file or die "Failed to open $file: $!\n";
my $file_content = do {local $/;<$fh>};
my $digest = md5($file_content);
push @{$digests_seen{$digest}},$file;
}
for my $digest (sort keys %digests_seen){
my $num_of_files = scalar @{$digests_seen{$digest}};
if ($num_of_files > 1){
print "Duplicates: \n";
print "[$_]\n" for @{$digests_seen{$digest}}
}
}
}
}
이는 MD5 합계를 사용하여 중복 파일을 식별하고 찾은 중복 파일 목록을 인쇄합니다. 각 파일 이름은 []
개행이 포함된 파일 이름을 시각적으로 확인하는 데 도움이 되도록 포함됩니다. 의도적으로 파일을 삭제하는 코드를 추가하지 않았습니다.이 코드는 완전히 테스트되지 않았습니다.. 결과 목록으로 무엇을 할지 결정하는 것은 여러분의 몫입니다.
파일이 크면 메모리와 CPU 사용량이 높아집니다. 위 스크립트는 각 파일을 메모리에 로드하고 전체 내용에 대해 MD5 합계를 수행합니다.
답변4
find . -type f |sort |tee f1 |uniq -i |comm -3 - f1
삭제하거나 무시할 파일 목록을 제공하며, 이를 파이프로 연결할 수 있습니다.무시 목록재동기화의 경우
24시간 후:
"이것은 비실용적입니다. 또 다른 발견이 필요합니다."라는 귀하의 의견에 대한 답변으로 결과를 이름 변경 피해를 줄 수 있는 것으로 연결하면 됩니다. 예를 들어 전체 솔루션은 하나의 명령줄에 있지만 읽기가 어렵습니다.
find . -type f |sort |tee f1 |uniq -i |comm -3 - f1|(n=0;while read a ;do n=$((${n}+1));echo mv ${a} `echo ${a}|tr \[:upper:\] \[:lower:\]`_renamed_${n};done)