将 comm 与 NULL 终止的记录一起使用

将 comm 与 NULL 终止的记录一起使用

结束于一个答案对于另一个问题,我想使用类似这样的结构来查找出现在list2但未出现在中的文件list1

( cd dir1 && find . -type f -print0 ) | sort -z > list1
( cd dir2 && find . -type f -print0 ) | sort -z > list2
comm -13 list1 list2

然而,我遇到了困难,因为我的版本comm无法处理以 NULL 结尾的记录。 (一些背景:我将计算列表传递给rm,所以我特别希望能够处理可能包含嵌入换行符的文件名。)

如果你想要一个简单的例子,试试这个

mkdir dir1 dir2
touch dir1/{a,b,c} dir2/{a,c,d}
( cd dir1 && find . -type f ) | sort > list1
( cd dir2 && find . -type f ) | sort > list2
comm -13 list1 list2

如果没有以 NULL 结尾的行,此处的输出是./d仅出现在 中的单个元素list2

我希望能够用来find ... -print0 | sort -z生成列表。

如何最好地重新实现一个等效项,输出出现在但未出现在 中comm的以 NULL 结尾的记录?list2list1

答案1

GNU comm(从 GNU coreutils 8.25 开始)现在有一个-z/--zero-terminated选项。

对于旧版本的 GNU comm,您应该能够交换 NUL 和 NL:

comm -13 <(cd dir1 && find . -type f -print0 | tr '\n\0' '\0\n' | sort) \
         <(cd dir2 && find . -type f -print0 | tr '\n\0' '\0\n' | sort) |
  tr '\n\0' '\0\n'

这种方式comm仍然适用于换行符分隔的记录,但输入中的实际换行符编码为 NUL,因此我们仍然可以安全地处理包含换行符的文件名。

您可能还需要将语言环境设置为,C因为至少在 GNU 系统和大多数 UTF-8 语言环境中,有不同的字符串排序相同,并且会导致此处出现问题。

这是一个非常常见的技巧(参见反转匹配行,NUL 分隔另一个例子是comm),但需要在输入中支持 NUL 的实用程序,这在 GNU 系统之外相对较少。


示例:

$ touch dir1/{①,②} dir2/{②,③}
$ comm -12 <(cd dir1 && find . -type f -print0 | tr '\n\0' '\0\n' | sort) \
           <(cd dir2 && find . -type f -print0 | tr '\n\0' '\0\n' | sort)  
./③
./②
$ (export LC_ALL=C
    comm -12 <(cd dir1 && find . -type f -print0 | tr '\n\0' '\0\n' | sort) \
             <(cd dir2 && find . -type f -print0 | tr '\n\0' '\0\n' | sort))
./②

2019年编辑:①②③的相对顺序已在较新版本的 GNU libc 中修复,但您可以使用

相关内容