Uso de comunicaciones con registros terminados en NULL

Uso de comunicaciones con registros terminados en NULL

Más adentrouna respuestaa una pregunta diferente, quería usar una estructura muy similar a esta para buscar archivos que aparecen y list2que no aparecen en list1:

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

Sin embargo, choqué contra una pared de ladrillos porque mi versión de commno puede manejar registros terminados en NULL. (Algunos antecedentes: estoy pasando una lista calculada a rm, por lo que particularmente quiero poder manejar nombres de archivos que podrían contener una nueva línea incrustada).

Si quieres un ejemplo fácil de resolver, prueba esto.

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

Sin líneas terminadas en NULL, la salida aquí es el elemento único ./dque aparece solo en list2.

Me gustaría poder usarlo find ... -print0 | sort -zpara generar las listas.

¿Cuál es la mejor manera de reimplementar un equivalente commque genere los registros terminados en NULL que aparecen list2pero que no aparecen en list1?

Respuesta1

GNU comm(a partir de GNU coreutils 8.25) ahora tiene una opción -z/ --zero-terminatedpara eso.

Para versiones anteriores de GNU comm, debería poder intercambiar NUL y 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'

De esa manera commtodavía funciona con registros delimitados por nuevas líneas, pero con nuevas líneas reales en la entrada codificadas como NUL, por lo que todavía estamos seguros con nombres de archivos que contienen nuevas líneas.

Es posible que también desee configurar la configuración regional Cporque en los sistemas GNU y al menos en la mayoría de las configuraciones regionales UTF-8, hay diferentes cadenas que ordenan lo mismo y causarían problemas aquí¹.

Ese es un truco muy común (verInvertir líneas coincidentes, separadas por NULpara otro ejemplo con comm), pero necesita utilidades que admitan NUL en su entrada, lo cual fuera de los sistemas GNU es relativamente raro.


¹ Ejemplo:

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

(edición 2019: El orden relativo de ①②③ se ha corregido en las versiones más recientes de GNU libc, pero puedes usar

información relacionada