比较 bash 脚本中不同目录中的 2 个基本文件名

比较 bash 脚本中不同目录中的 2 个基本文件名

我正在尝试编写一个 bash 脚本,该脚本采用 2 个文件目录作为参数。然后循环遍历第一个目录中的所有文件,查看第二个目录中是否有对应的文件。如果不存在具有相同基本名称的文件,则删除该文件。现在这就是我所拥有的,但它最终没有做任何事情,因为它认为由于某种原因它们都是匹配的。

echo "$directory1"
echo "$directory2"
for f1 in "$directory1/*"
do
    echo $f1
    match=no
    for f2 in "$directory2/*"
    do
        echo $f2
        name1="$(basename "$f1")"
        name2="$(basename "$f2")"
        if [ "$name1" == "$name2" ]
        then
            match=yes
            break
        fi
    done
    echo "$match"
    if [[ "$match" == no ]]
    then
        rm "$f2"
    fi
done

答案1

这是一种方法,使用 find 列出每个目录中的文件,使用 basename 和 sort 对它们进行排序。然后将每个列表提供给 comm,并仅打印第一个目录中存在的文件,而不打印第二个目录中存在的文件。一旦列表看起来符合您的预期,您可以将其通过管道传递给 rm。

comm -2 -3  <(find "$directory1" -maxdepth 1 -type f | xargs -n1 -d\\n basename | sort) <(find "$directory2" -maxdepth 1 -type f | xargs -n1 -d\\n basename | sort)  | xargs -d\\n printf "$directory1/%s\n" | xargs -d\\n rm

注意:正如@LeviUzodike 指出的那样,您想要删除第一个目录中在第二个目录中找不到的文件,因此我进行了适当的更改。我还避免使用 -a 参数作为基本名称

答案2

要纠正您原来的解决方案:

  1. 就像@Kusalananda所说,你应该分别将"$directory1/*""$directory2/*"变成"$directory1"/*"$directory2"/*,否则 for 循环将尝试查找字面名为“ *”的文件。
  2. 如果我正确理解你的目标,该行rm "$f2"应该是rm "$f1",否则程序将只删除与$directory2不匹配的最后一个文件$f1,什么时候$f1应该删除该文件,因为它与$directory2.

只需进行这两个修复,您的脚本就应该可以工作(假设directory1directory2被分配了正确的值。)

但就像 @Kusalananda 所说,你不必再做一个循环。我认为@Kusalananda 建议你做这样的事情(取出echos 并纠正已经提到的错误):

for f1 in "$directory1"/*
do
    # ls "$directory2"/$(basename "$f1") &>/dev/null
    # if [[ $? != 0 ]]
    # EDIT below from @Kusalananda
    if [[ -e "$directory2/$(basename "$f1")" ]]
    then
        rm "$f1"
    fi
done

从技术上讲,它是另一个 for 循环(因为我相信ls在幕后做了一个 for 循环),但它看起来干净得多。抱歉,如果有额外的报价。我不是最擅长知道如何使用它们。

答案3

zsh使用及其数组减法运算符会更容易${array1:|array2}

#! /bin/zsh -
dir1=${1:?} dir2=${2:?}

dir1_files=(${dir1%/}/*(DN:t))
dir2_files=(${dir2%/}/*(DN:t))

only_in_dir2=(${dir2:|dir1})

(($#only_in_dir2 == 0)) ||
  rm -rf -- $dir2/$^only_in_dir2

使用bashGNU 实用程序,等效操作可能类似于:

#! /bin/bash -
dir1=${1:?} dir2=${2:?}
export LC_ALL=C

chdir() {
  case $1 in
    (/*) CDPATH= cd -P "$1";;
    ("") echo >&2 invalid empty directory; false;;
    (*)  CDPATH= cd -P "./$1";;
  esac
}
list_files() (
  shopt -s nullglob dotglob # equivalent of zsh's ND
  chdir "$1" &&
    set -- * &&
    (($# > 0)) &&
    printf '%s\0' "$@"
)
    
comm -z13 <(list_files "$dir1") <(list_files "$dir2") |
  (chdir "$dir2" && xargs -r0 rm -rf --)

是的,不管你信不信,这个chdir函数就是你如何在 POSIX shell 中更改到任意目录并POSIX 在标准的下一个修订版中将推荐什么

请注意,在这种zsh情况下,如果dir1不可读,则其中的所有文件dir2将被删除。

有了一个,如果不可读bash就会出现这种情况dir1或不可搜索(根据cd需要搜索允许)。

如果您想在尝试构建匹配文件列表时出现错误时摆脱困境,您可以执行以下操作:

#! /bin/zsh -
dir1=${1:?} dir2=${2:?}
zmodload zsh/system || exit

ERRNO=0
dir1_files=(${dir1%/}/*(DN:t))
dir2_files=(${dir2%/}/*(DN:t))

if ((ERRNO)); then
  syserror -p "An error occurred when trying to read the directories: "
  exit 1
fi

only_in_dir2=(${dir2:|dir1})

(($#only_in_dir2 == 0)) ||
  rm -rf -- $dir2/$^only_in_dir2

相关内容