將 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 中修復,但您可以使用

相關內容