Usando comm com registros terminados em NULL

Usando comm com registros terminados em NULL

Emuma respostapara uma pergunta diferente, eu queria usar uma estrutura parecida com esta para encontrar arquivos que aparecem em list2que não aparecem em list1:

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

No entanto, bati em uma parede de tijolos porque minha versão commnão consegue lidar com registros terminados em NULL. (Alguns antecedentes: estou passando uma lista computada para rm, então, particularmente, quero ser capaz de lidar com nomes de arquivos que possam conter uma nova linha incorporada.)

Se você quiser um exemplo fácil de resolver, tente este

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

Sem linhas terminadas em NULL, a saída aqui é o único elemento ./dque aparece apenas em list2.

Gostaria de poder usar find ... -print0 | sort -zpara gerar as listas.

Qual a melhor forma de reimplementar um equivalente commque produza os registros terminados em NULL que aparecem em, list2mas que não aparecem em list1?

Responder1

GNU comm(a partir do GNU coreutils 8.25) agora tem uma opção -z/ --zero-terminatedpara isso.

Para versões mais antigas do GNU comm, você poderá trocar NUL e 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'

Dessa forma commainda funciona com registros delimitados por novas linhas, mas com novas linhas reais na entrada codificadas como NULs, portanto ainda estamos seguros com nomes de arquivos contendo novas linhas.

Você também pode querer definir a localidade como Cporque em sistemas GNU e na maioria das localidades UTF-8, pelo menos, existem strings diferentes que classificam da mesma forma e causariam problemas aqui¹.

Esse é um truque muito comum (vejaInverter linhas correspondentes, separadas por NULpara outro exemplo com comm), mas precisa de utilitários que suportem NUL em sua entrada, o que fora dos sistemas GNU é relativamente raro.


¹ Exemplo:

$ 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))
./②

(Edição de 2019: A ordem relativa de ①②③ foi corrigida nas versões mais recentes da GNU libc, mas você pode usar

informação relacionada