Подсчет элементов в файле с разделителями

8

У меня есть сценарий оболочки, который используется find -print0для сохранения списка файлов для обработки во временный файл. Как часть журнала я хотел бы вывести количество найденных файлов, и поэтому мне нужен способ получить это количество. Если бы эта -print0опция не использовалась для безопасности, я мог бы использовать ее wc -lдля подсчета.

qqx
источник
Связанный: Как сделать headи tailна вводе с нулем в bash?
Стефан Шазелас

Ответы:

11

Некоторые варианты:

tr -cd '\0' | wc -c

tr '\n\0' '\0\n' | wc -l      # Generic approach for processing NUL-terminated
                              # records with line-based utilities (that support
                              # NUL characters in their lines like GNU ones).

grep -cz '^'                  # GNU grep

sed -nz '$='                  # recent GNU sed, no output for empty input

awk -vRS='\0' 'END{print NR}' # not all awk implementations

Обратите внимание, что для ввода, который содержит данные после последнего символа NUL (или непустого ввода без символов NUL), trподходы всегда будут подсчитывать количество символов NUL, но подходы awk/ sed/ grepбудут считать дополнительную запись для этих дополнительных байтов. ,

Стефан Шазелас
источник
Я измерил их на 5 ГБ случайных данных ( head -c 5G /dev/urandom > f). Результаты: grep 1.7s (тоже самое grep -Fcz '') • tr + wc-c 7.7s • tr + wc-l 7.4s • sed 34.7s • awk 1m11.7s
Socowi
@Socowi, YMMV с реализацией и локалью. С помощью GNU awkвы захотите установить языковой стандарт C(или любой, в котором не используются многобайтовые символы),LC_ALL=C awk ... < f
Стефан Шазелас
Спасибо за подсказку. Я уже использовал LC_ALL=Cна sortкотором он не скорость вещи, поэтому К счастью , я до сих пор есть файл с раньше: LC_ALL=C awk ...принимает 6.7s.
Socowi
4

Лучший метод, который я смог придумать - это использовать grep -zc '.*'. Это работает, но неправильно использовать grep с шаблоном, который будет соответствовать чему угодно.

qqx
источник
1

С perl:

perl -0ne 'END {print $.}'

или:

perl -nle 'print scalar split "\0"'

или:

perl -nle 'print scalar unpack "(Z*)*", $_'
cuonglm
источник
Первый будет считать дополнительную запись, если есть данные после последнего NUL. Два других не работают, если ввод содержит символы новой строки.
Стефан Шазелас
@ StéphaneChazelas: О, мой плохой. Не могли бы вы дать какие-либо улучшения?
cuonglm
Я бы оставил первый и упомянул тот факт, что он считает запись без разделителя (вопреки wc -l) запиской (как это может быть необходимо).
Стефан Шазелас