首页 > 解决方案 > 如何检查两个文件在 Bash 中是否具有相同的确切名称

问题描述

我有一个代码试图检查两个不同文件夹中的文件是否具有相同的名称,如果它们没有相同的名称,它会从第二个目录中删除不匹配的文件。

但是我的代码无法正常工作。

#!/bin/bash

mn_dir=$1
en_dir=$2

for mn_file in $mn_dir/*;
do
    for en_file in $en_dir/*;
    do
        if [[ "$mn_file"=="$en_file" ]]; then
            echo "$mn_file AND $en_file"
        else
            rm $en_file
        fi
    done
done

输出如下所示,mn/1.txt AND en/10.txt其中 1.txt 和 10.txt 名称不同。如何检查两个文件是否同名?

mn/1.txt AND en/1.txt
mn/1.txt AND en/10.txt
mn/1.txt AND en/2.txt
mn/1.txt AND en/3.txt
mn/1.txt AND en/4.txt
mn/1.txt AND en/5.txt
mn/1.txt AND en/6.txt
mn/1.txt AND en/7.txt
mn/1.txt AND en/8.txt
mn/1.txt AND en/9.txt
mn/2.txt AND en/1.txt
mn/2.txt AND en/10.txt
mn/2.txt AND en/2.txt
mn/2.txt AND en/3.txt
mn/2.txt AND en/4.txt
mn/2.txt AND en/5.txt
mn/2.txt AND en/6.txt
mn/2.txt AND en/7.txt
mn/2.txt AND en/8.txt
mn/2.txt AND en/9.txt
mn/3.txt AND en/1.txt
mn/3.txt AND en/10.txt
mn/3.txt AND en/2.txt
mn/3.txt AND en/3.txt
mn/3.txt AND en/4.txt
mn/3.txt AND en/5.txt
mn/3.txt AND en/6.txt
mn/3.txt AND en/7.txt
mn/3.txt AND en/8.txt
mn/3.txt AND en/9.txt
mn/4.txt AND en/1.txt
mn/4.txt AND en/10.txt
mn/4.txt AND en/2.txt
mn/4.txt AND en/3.txt
mn/4.txt AND en/4.txt
mn/4.txt AND en/5.txt
mn/4.txt AND en/6.txt
mn/4.txt AND en/7.txt
mn/4.txt AND en/8.txt
mn/4.txt AND en/9.txt
mn/5.txt AND en/1.txt
mn/5.txt AND en/10.txt
mn/5.txt AND en/2.txt
mn/5.txt AND en/3.txt
mn/5.txt AND en/4.txt
mn/5.txt AND en/5.txt
mn/5.txt AND en/6.txt
mn/5.txt AND en/7.txt
mn/5.txt AND en/8.txt
mn/5.txt AND en/9.txt

标签: bashshell

解决方案


TL;博士

解决实际问题,请参阅详细信息以获取另一个可能更接近实际目标的选项。

这将删除所有不在其中的文件en_dirmn_dir但不在其中的文件mn_dir不在en_dir

#!/bin/bash

if [ $# -ne 2 ]; then
  echo "Usage: $0 <mn_dir> <en_dir>"
  exit 1
fi

mn_dir=$1
en_dir=$2

for en_file in "$en_dir"/*;
do
    mn_file="$mn_dir/${en_file##*/}"
    if [ -e "$mn_file" ]; then
        echo "$mn_file AND $en_file"
    else
        rm "$en_file"
    fi
done

对于这两种方式,要么:

./script.bash dir1 dir2
./script.bash dir2 dir1

或执行以下操作:

#!/bin/bash

if [ $# -ne 2 ]; then
  echo "Usage: $0 <dir1> <dir2>"
  exit 1
fi

del_dest_extra_files() {
    if [ $# -ne 2 ]; then
        return 1
    fi

    mn_dir=$1
    en_dir=$2

    for en_file in "$en_dir"/*;
    do
        mn_file="$mn_dir/${en_file##*/}"
        if [ -e "$mn_file" ]; then
            echo "$mn_file AND $en_file"
        else
            rm "$en_file"
        fi
    done
}

del_dest_extra_files "$1" "$2"
del_dest_extra_files "$2" "$1"

注意 1:这不会验证文件实际上是相同的、内容方面的,甚至是类型方面的(即同名的目录和文件仍然匹配)

注意 2:我已经引用了文件名安全的变量


细节

正如@jeremysprofile 建议的那样,您可以使用basename(1)。此外,您需要在 周围有空格==,例如:

if [[ "$(basename $mn_file)" == "$(basename $en_file)" ]]

但是,basename子外壳很昂贵,如果这只是为了bash,不需要是 POSIX,您可以使用bash删除匹配前缀模式

if [[ "${mn_file##*/}" == "${en_file##*/}" ]]

最后,如果您使用文件存在测试( ),则不需要多个文件循环,-e因此您可以:

#!/bin/bash

mn_dir=$1
en_dir=$2

for mn_file in "$mn_dir"/*;
do
    en_file="$en_dir/${mn_file##*/}"
    if [ -e "$en_file" ]; then
        echo "$mn_file AND $en_file"
    else
        rm "$en_file"
    fi
done

更新:鉴于您正在尝试删除不在源中的目标目录文件,我们需要翻转我们正在循环的目录:

#!/bin/bash

mn_dir=$1
en_dir=$2

for en_file in "$en_dir"/*;
do
    mn_file="$mn_dir/${en_file##*/}"
    if [ -e "$mn_file" ]; then
        echo "$mn_file AND $en_file"
    else
        rm "$en_file"
    fi
done

危险:仅使用一个参数运行此脚本可能会删除根目录中的文件(如果有的话)。您应该确保参数不为 NULL:

#!/bin/bash

if [ $# -ne 2 ]; then
  echo "Usage: $0 <mn_dir> <en_dir>"
  exit 1
fi

mn_dir=$1
en_dir=$2

for en_file in "$en_dir"/*;
do
    mn_file="$mn_dir/${en_file##*/}"
    if [ -e "$mn_file" ]; then
        echo "$mn_file AND $en_file"
    else
        rm "$en_file"
    fi
done

解决可能更容易的目标的另一种方法,特别是如果您想考虑内容,是利用diff进行比较:

#!/bin/bash

if [ $# -ne 2 ]; then
    echo "Usage: $0 <dir1> <dir2>"
    exit 1
fi

while read -r f; do
    rm "${f}"
done < <(
    diff -qr "${1}" "${2}"\
    |sed -n 's#^Only in \(.*\): \(.*\)$#\1/\2#p'
)

这将删除所有dir1不在的dir2文件和所有dir2不在的文件,dir1 除非它们不包含相同的数据。


推荐阅读