NULL で終了するレコードでの comm の使用

NULL で終了するレコードでの comm の使用

終わりました答えlist2別の質問ですが、に表示されて には表示されないファイルを見つけるために、次のような構造を使用したいと考えましたlist1

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

しかし、私のバージョンの は NULL で終了するレコードを処理できないため、行き詰まってしまいましたcomm。(背景: 計算されたリストを に渡すので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リストを生成するために使用したいと思います。

には表示されるが には表示されないcommNULL 終了レコードを出力すると同等のものを最も効果的に再実装するにはどうすればよいでしょうか?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の新しいバージョンでは、①②③の相対的な順序は修正されていますが、

関連情報