Usando comm com registros terminados em NULL

1

Em uma resposta a uma pergunta diferente, eu queria usar uma estrutura como essa 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, eu bati em um muro de tijolos porque a minha versão de comm não pode lidar com registros terminados em NULL. (Alguns antecedentes: estou passando uma lista computada para rm , então eu 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 trabalhar, tente isso

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.

Como é melhor reimplementar um equivalente a comm que produza os registros terminados em NULL que aparecem em list2 , mas que não aparecem em list1 ?

    
por roaima 30.05.2018 / 18:03

1 resposta

1

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

Para versões anteriores do GNU comm , você deve ser capaz de trocar NUL e NL:

comm -13 <(cd dir1 && find . -type f -print0 | tr '\n
$ touch dir1/{①,②} dir2/{②,③}
$ comm -12 <(cd dir1 && find . -type f -print0 | tr '\n
comm -13 <(cd dir1 && find . -type f -print0 | tr '\n
$ touch dir1/{①,②} dir2/{②,③}
$ comm -12 <(cd dir1 && find . -type f -print0 | tr '\n%pre%' '%pre%\n' | sort) \
           <(cd dir2 && find . -type f -print0 | tr '\n%pre%' '%pre%\n' | sort)  
./③
./②
$ (export LC_ALL=C
    comm -12 <(cd dir1 && find . -type f -print0 | tr '\n%pre%' '%pre%\n' | sort) \
             <(cd dir2 && find . -type f -print0 | tr '\n%pre%' '%pre%\n' | sort))
./②
' '%pre%\n' | sort) \ <(cd dir2 && find . -type f -print0 | tr '\n%pre%' '%pre%\n' | sort) | tr '\n%pre%' '%pre%\n'
' '%pre%\n' | sort) \ <(cd dir2 && find . -type f -print0 | tr '\n%pre%' '%pre%\n' | sort) ./③ ./② $ (export LC_ALL=C comm -12 <(cd dir1 && find . -type f -print0 | tr '\n%pre%' '%pre%\n' | sort) \ <(cd dir2 && find . -type f -print0 | tr '\n%pre%' '%pre%\n' | sort)) ./②
' '%pre%\n' | sort) \ <(cd dir2 && find . -type f -print0 | tr '\n%pre%' '%pre%\n' | sort) | tr '\n%pre%' '%pre%\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 nos sistemas GNU e na maioria das localidades UTF-8 existem pelo menos cadeias de caracteres diferentes que ordenam o mesmo e causariam problemas aqui.

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

¹ Exemplo:

%pre%     
por 30.05.2018 / 18:33

Tags