
Emuma respostapara uma pergunta diferente, eu queria usar uma estrutura parecida com esta para encontrar arquivos que aparecem em list2
que 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 comm
nã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 ./d
que aparece apenas em list2
.
Gostaria de poder usar find ... -print0 | sort -z
para gerar as listas.
Qual a melhor forma de reimplementar um equivalente comm
que produza os registros terminados em NULL que aparecem em, list2
mas que não aparecem em list1
?
Responder1
GNU comm
(a partir do GNU coreutils 8.25) agora tem uma opção -z
/ --zero-terminated
para 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 comm
ainda 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 C
porque 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