我想编写一个 bash 脚本来在一个目录中查找这些文件,但不在另一个目录中查找这些文件。
下面的脚本可以工作吗?什么时候不呢?
for i in "$1"/*; do
f=$(basename $i);
if [ ! -e "$2"/"$f" ]
then
echo $f
fi
done
我听说diff
也可以找到两个目录内容之间的差异。它也能解决同样的任务吗?
或者其他一些工具?
谢谢。
答案1
是的,您可以用于diff
此目的。很简单:
diff -rq dir1 dir2
该-r
选项diff
也告诉我们递归到子目录。该-q
选项指示diff
仅在文件不同时报告。
dir1
当我想找出哪些文件在 中,但不在 中dir2
,反之亦然时,我通常使用这两个选项。 (-r
如果您不想递归到子目录,但只考虑两个目录的直接内容,也可以删除该参数。)
请注意,这将显示存在于dir1
但不存在于 中的文件dir2
,以及存在于 中dir2
但不存在于 中的文件dir1
,例如:
$ diff -rq /tmp/dir1/ /tmp/dir2/
Only in /tmp/dir1/: file1
Only in /tmp/dir2/: file2
Only in /tmp/dir2/: file3
如果您只需要其中一个方向(例如,位于dir1
但不在 中的文件dir2
)并仅获取文件名列表(没有“仅在...”混乱),您当然可以尝试使用来按摩diff
的输出grep
、等,但在这种情况下,您最好一开始就不要使用sed
,而使用 Stéphane Chazelas 的解决方案。awk
diff
答案2
如果您的文件名不包含换行符,您可以执行以下操作:
(export LC_ALL=C; comm -23 <(ls -A dir1) <(ls -A dir2))
找出 中的哪些文件dir1
在 中找不到dir2
。
对于任意文件名,您可以使用数组减法功能zsh
:
dir1_files=(dir1/*(DN:t)) dir2_files=(dir2/*(DN:t))
dir1_and_not_dir2_files=(${dir1_files:|dir2_files})
(更改*
为**/*
递归文件列表)
或者使用 bash4.4+ 和最新版本的 GNU 实用程序:
readarray -td '' dir1_and_not_dir2_files < <(
export LC_ALL=C
shopt -s nullglob dotglob
comm -z23 <(printf '%s\0' dir1/* | cut -zd/ -f2-) \
<(printf '%s\0' dir2/* | cut -zd/ -f2-)
)
(使用globstar
选项并替换*
为**
递归列表)。
需要它LC_ALL=C
至少有两个原因:
文件名可以包含任何字节序列(0 或(基于 ASCII 的系统上的 0x2F)的值除外
/
),而comm
是文本实用程序,因此对于那些不格式化有效字符的字节序列未指定行为。在 C 语言环境中,所有字符都是单字节且所有字节都是有效字符(尽管可能未定义),sp 任何文件名都是有效文本(还考虑到最大文件名长度通常明显小于最大文本行长度) 。更重要的是,
comm
需要排序的输入,但在某些语言环境中,某些字符具有未定义的排序顺序或与其他字符排序相同,这会造成混淆comm
。例如,在 en_GB.UTF-8 语言环境的 GNU 系统上:$ ls dir1 dir2 dir1: