別の質問に対する 回答 で、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
を使用してリストを生成できるようにしたいと思います。
list2
に表示されるが、list1
には表示されないNULLで終了するレコードを出力する、comm
に相当するものを再実装するにはどうすればよいですか?
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としてエンコードされているため、改行を含むファイル名でも安全です。
GNUシステムと少なくともほとんどのUTF-8ロケールでは、同じように並べ替えるさまざまな文字列があり、ここで問題が発生するため、ロケールをC
に設定することもできます¹。
これは非常に一般的なトリックです(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 edit:①②③の相対的な順序は、GNU libcの新しいバージョンで修正されましたが、????????を使用できます。 ????代わりに、たとえば、Unicodeコードポイントの95%などの問題がまだ残っている新しいバージョン(少なくとも2.30)では)