我正在尝试编写一个 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
要纠正您原来的解决方案:
- 就像@Kusalananda所说,你应该分别将
"$directory1/*"
和"$directory2/*"
变成"$directory1"/*
和"$directory2"/*
,否则 for 循环将尝试查找字面名为“*
”的文件。 - 如果我正确理解你的目标,该行
rm "$f2"
应该是rm "$f1"
,否则程序将只删除与$directory2
不匹配的最后一个文件$f1
,什么时候$f1
应该删除该文件,因为它与$directory2
.
只需进行这两个修复,您的脚本就应该可以工作(假设directory1
和directory2
被分配了正确的值。)
但就像 @Kusalananda 所说,你不必再做一个循环。我认为@Kusalananda 建议你做这样的事情(取出echo
s 并纠正已经提到的错误):
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
使用bash
GNU 实用程序,等效操作可能类似于:
#! /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