Использование comm с записями, завершающимися NULL

Использование comm с записями, завершающимися NULL

ВОтветна другой вопрос, я хотел использовать структуру, похожую на эту, чтобы найти файлы, которые появляются в , list2но не появляются в list1:

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

Однако я натолкнулся на глухую стену, поскольку моя версия 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для генерации списков.

Как лучше всего перереализовать эквивалент, commкоторый выводит записи с завершающим NULL-символом, которые появляются в , list2но не появляются в list1?

решение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, но вы можете использовать

Связанный контент