【问题标题】:Bash script to compare files用于比较文件的 Bash 脚本
【发布时间】:2017-01-01 02:20:48
【问题描述】:

我有一个文件夹,里面有很多重复的旧照片。手动排序需要很长时间,所以我想借此机会使用 bash。

现在我有代码:

#!/bin/bash

directory="~/Desktop/Test/*"
for file in ${directory};
do
    for filex in ${directory}:
    do
        if [ $( diff {$file} {$filex} ) == 0 ]
        then
            mv ${filex} ~/Desktop
            break
        fi
    done
done 

并获得退出代码:

diff: {~/Desktop/Test/*}: No such file or directory
diff: {~/Desktop/Test/*:}: No such file or directory
File_compare: line 8: [: ==: unary operator expected

我尝试修改我在网上找到的工作代码,但它似乎总是吐出一些这样的错误。我猜这是嵌套 for 循环的问题?

另外,为什么调用变量的方式似乎不同?我见过使用${file}, "$file", and "${file}" 的例子。

【问题讨论】:

  • 尝试分配完整路径。 directory="/home/someuser/Desktop/Test/*" 或者,不要使用引号。 directory=~/Desktop/Test/* 引号似乎会导致扩展波浪号出现问题。
  • 我现在无法对其进行测试,但我想我尝试过并得到了同样的错误。

标签: bash sorting


【解决方案1】:

您将{} 放在错误的位置:

if [ $( diff {$file} {$filex} ) == 0 ]

他们应该在:

if [ $( diff ${file} ${filex} ) == 0 ]

(虽然大括号现在是可选的),但你应该在文件名中允许空格:

if [ $( diff "${file}" "${filex}" ) == 0 ]

现在它根本无法正常工作,因为当diff 没有发现任何差异时,它不会生成任何输出(并且您会收到错误,因为== 运算符不希望其左侧没有任何内容)。您可以通过双引号 $(…) (if [ "$( diff … )" == "" ]) 的值来修复它,但您应该简单直接地测试 diff 的退出状态:

if diff "${file}" "${filex}"
then : no difference
else : there is a difference
fi

也许为了比较图像,您应该使用cmp(在静音模式下)而不是diff

if cmp -s "$file" "$filex"
then : no difference
else : there is a difference
fi

【讨论】:

  • 我会尽可能地展示它。你能解释一下为什么你有no difference吗?我不明白这意味着“没有输出”。
  • 我的意思是if diff … 测试diff 命令的退出状态,如果两个文件相同,diff 返回 0(成功)和非零(失败)状态如果文件不同(或未找到文件,或……)。因此,if 测试在文件相同(或它们之间没有区别)时执行 then 代码,或者在文件不同时执行 else 代码。 : 命令是一个内置的 shell,它对其参数进行评估然后成功——在这种情况下,它是一个空操作,你可以用你想要执行文件的代码替换它是相同的。等等。
  • 如果文件之间存在差异,diff 命令会将差异打印到标准输出。如果您不想看到,可以将此类输出重定向到 /dev/null。 OTOH,使用cmp -s 已经通过不生成任何输出来解决这个问题。
  • 请注意,Gordon Davisson 在他的answer 中也提出了一些有效的问题——您应该注意这两个问题。
【解决方案2】:

除了乔纳森·莱弗勒指出的问题:

directory="~/Desktop/Test/*"
for file in ${directory};

~* 不会在双引号内展开;当您使用不带引号的变量时,* 将得到扩展,但由于 ~ 不会,它会在实际名为“~”的目录下查找文件(不是你的主目录) ,它不会找到任何匹配项。此外,正如 Jonathan 指出的那样,使用不带双引号的变量(如 ${directory})会使您在使用包含空格或其他一些元字符的文件名时遇到麻烦。更好的方法是不要将通配符放在变量中,在引用变量时使用它,变量用双引号括起来,* 在它们之外:

directory=~/"Desktop/Test"
for file in "${directory}"/*;

哦,还有一个注意事项:在脚本中使用mv 时,最好使用mv -i 以避免意外覆盖另一个同名文件。

并且:使用shellcheck.net 对代码进行健全性检查并指出常见错误。

【讨论】:

    【解决方案3】:

    如果您只是想知道两个文件是否不同,cmp 是最佳选择。它的优点是:

    1. 它适用于文本和二进制文件,不像 diff 仅适用于文本文件

    2. 找到第一个差异后停止,因此效率很高

    所以,你的代码可以写成:

    if ! cmp -s "$file" "$filex"; then
      # files differ...
      mv "$filex" ~/Desktop
    
      # any other logic here
    fi
    

    希望这会有所帮助。我不明白您要对循环做什么,因此没有编写完整的代码。

    【讨论】:

    • 我正在尝试获取目录中的每个file 并将其与下一行的所有其他文件进行比较,同时删除重复项。现在使用代码不起作用;它说一切都是重复的。这里:for file in "${directory}"/*; do for filex in "${directory}"/*; do if cmp -s "$file" "$filex" then : else mv -i "$filex" ~/Desktop echo "$filex" fi done done
    • 可能您不应该将文件与其自身进行比较。
    • 哦,我明白了。它每次都将第一个文件与自身进行比较......这确实使事情复杂化。有没有简单的方法可以避免这种情况?
    • 我已经尝试过:if [ cmp -s "$file" "$filex" ] || ["$file" != "$filex" ]; ,但仍然出现问题。
    • 只有当文件名不同时才进行比较 t.所以你需要 && 而不是 ||。
    【解决方案4】:

    您可以使用diff "$file" "$filex" &>/dev/null 并使用$? 获取最后的命令结果:

    #!/bin/bash
    
    SEARCH_DIR="."
    DEST_DIR="./result"
    
    mkdir -p "$DEST_DIR"
    
    directory="."
    
    ls $directory | while read file;
    do
        ls $directory | while read filex;
        do
            if [ ! -d "$filex" ] && [ ! -d "$file" ] && [ "$filex" != "$file" ];
            then
    
                diff "$file" "$filex" &>/dev/null
    
                if [ "$?" == 0 ];
                then
                    echo "$filex is a duplicate. Copying to $DEST_DIR"
                    mv "$filex" "$DEST_DIR"
                fi
            fi
        done
    done 
    

    请注意,您还可以使用 fslintfdupes 实用程序来查找重复项

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-09
      • 2022-10-18
      • 2014-02-03
      • 2012-09-06
      • 2018-07-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多