【问题标题】:How to separate individual file names in a list when they contain spaces如何在列表中包含空格时分隔单个文件名
【发布时间】:2015-03-10 15:57:07
【问题描述】:

我正在编写一个 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

如何将文件名用引号括起来,以便将它们清楚地标识为一个块,以便我能够根据这些列表执行操作?

【问题讨论】:

  • echo 毫无意义。如果不逐行操作,您就无法做到这一点(即使只有在您的文件名不能包含换行符时才有效)。如果可以,您需要以 NUL 分隔的条目。见mywiki.wooledge.org/BashFAQ/001

标签: regex bash shell unix awk


【解决方案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
【解决方案2】:

我知道这不是你问的,但我个人建议 - 使用 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 中的所有非唯一条目都已删除)

【讨论】:

    猜你喜欢
    • 2022-12-05
    • 1970-01-01
    • 2018-11-20
    • 1970-01-01
    • 2010-09-28
    • 1970-01-01
    • 2021-05-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多