我正在编写一个bash脚本,用于根据SHA-1哈希值生成重复文件列表。然后,我希望从包含目录中所有文件名的数组中删除这些重复项,因此我只能将非重复文件复制到新目录。
我面临的问题是,许多文件的名称中都包含空格,因此我不知道如何将它们与列表分开。
# Fill array with all file names in directory
files=(*)
#find all repeating sha-1 values
repeats=$(echo $(find -type f -exec sha1sum '{}' ';' | sort | uniq --all-repeated=separate -w 40 ))
由于名称中的空格,输出采用以下格式:
1386d44b318730ffa98a34176d4e8b7eab8e02a4 ./Forensic Scripting 01 - Introduction to Developing Software and Shell Scripting (1).ppt 1386d44b318730ffa98a34176d4e8b7eab8e02a4 ./Forensic Scripting 01 - Introduction to Developing Software and Shell Scripting.ppt 2f4fc07ee944d666c34b0dfeeda90ad1c5cb9e71 ./kravica_waterfall_bosnia (copy).jpg 2f4fc07ee944d666c34b0dfeeda90ad1c5cb9e71 ./kravica_waterfall_bosnia.jpg 45f478cedd980ff2313f05fd0997a08492b9b21b ./canada-niagarafalls_-5 (another copy).jpg 45f478cedd980ff2313f05fd0997a08492b9b21b ./canada-niagarafalls_-5 (copy).jpg 45f478cedd980ff2313f05fd0997a08492b9b21b ./canada-niagarafalls_-5.jpg a3c6c5b749ce43cc3dade17230580b5ecf4d1557 ./frink (copy).png a3c6c5b749ce43cc3dade17230580b5ecf4d1557 ./frink.png d6039f1932dc2bb6fecfa41c02a7e9bc6656c621 ./UK - Associate Guide (Final Version -Effective Date 13 Feb 2012) (copy).pdf d6039f1932dc2bb6fecfa41c02a7e9bc6656c621 ./UK - Associate Guide (Final Version -Effective Date 13 Feb 2012).pdf
如何将文件名包装在引号中,以便将它们清楚地标识为一个块,以便我能够根据这些列表执行操作?
答案 0 :(得分:1)
如果你有bash 4,你可以使用关联数组,这会使这个问题变得更简单。 (下面的bash 3解决方案)。
例如:
# The output array
declare -a nondups=()
# An associative array which maps checksum to filename
declare -A checksum
for file in *; do
# Make sure it's not a directory
if [[ -f "$file" ]]; then
chk=$(sha1sum "$file")
# We just want the checksum
chk=${chk%% *}
if ! [[ -v checksum[$chk] ]]; then
# -v doesn't work on subscripts before v4.3. Alternative:
# if [[ -z ${checksum[$chk]} ]]; then
# We've never hit this checksum before
nondups+=($file) # Add it to the list
checksum[$chk]=1 # Mark checksum as seen
fi
fi
done
如果您想要执行递归文件列表,就像find
那样,您可以使用globstar
shell选项来执行递归列表。确保您已完成shopt -s globstar
,然后将for
循环更改为for file in **; do
如果您没有bash 4,可以将checksum
设为一个简单的字符串变量,并使用子字符串匹配进行检查:
if [[ $checksum == *${chk}* ]]; then
# We've never hit this checksum before
nondups+=($file) # Add it to the list
checksum+=" $chk" # Mark checksum as seen
fi
答案 1 :(得分:0)
我知道它并不是你提出的要求,但我个人建议 - 使用perl。
#!/usr/bin/perl
use strict;
use warnings;
use Digest::SHA1 qw ( sha1_hex );
my $search = "*";
my %digests;
while ( my $filename = glob ( $search ) ) {
next if -d $filename;
open ( my $input, "<", $filename ) or warn $!;
my $sha_sum = sha1_hex ( <$input> );
close ( $input );
if ( $digests{$sha_sum} ) { print "$filename matches $digests{$sha_sum}\n"; }
$digests{$sha_sum} = $filename;
print "$filename $sha_sum\n";
}
这样 - 您可以根据需要操纵$filename
,而不必担心空格等。(这不会打印所有重复项,因为它只跟踪最后一个重复项) 。
完成后,您可以:
foreach my $filename ( values %digests ) {
print "$filename is unique\n";
}
(因为%digests
中的任何非唯一条目已被删除)