Как найти файлы с 100% символов NUL в их содержании?

Я предлагаю:

#!/bin/bash

if (($EUID != 0)); then
  if [[ -t 1 ]]; then
    sudo "$0" "$@"
  else
    exec 1>output_file
    gksu "$0 $@"
  fi
  exit
fi

# some example stuff
ls -l /root
echo "app: $0"
for f; do
  echo ">$f<"
done
16
20.12.2012, 14:45
9 ответов

Вы можете grep для ␀ символов с помощью Perl regex режим:

$ echo -ne "\0\0" > nul.bin
$ echo -ne "\0x\0" > non-nul.bin
$ grep -P "[^\0]" *.bin
Binary file non-nul.bin matches

Таким образом, можно использовать это:

for path in *.foo
do
    grep -P "[^\0]" "$path" || echo "$path"
done
10
27.01.2020, 19:48
  • 1
    я получаю неожиданные результаты, с помощью GNU grep 2.5.4. Независимо от того, использую ли я --binary-files=text или --binary-files=binary, это дает a true результат для всех непустых значений данных, например. "\0\0", "\0x\0", "abcd"... Точный код, который я использовал: for typ in binary text ;do for dat in '\0\0' '\0x\0' 'abcd' '' ;do printf "$dat" >f; grep --binary-files=$typ -P '[^\0]' f >/dev/null && echo true || echo false; done; done –  Peter.O 21.12.2012, 05:37
  • 2
    я теперь далее попробовал GNU grep) 2.10. Эта более поздняя версия действительно дает ожидаемые результаты... так, запоздалый +1 –  Peter.O 21.12.2012, 05:44
  • 3
    Сбои на файле, созданном с printf '\0\n\0\0\n\n' > file или printf '\n' > file для этого вопросы. –  Stéphane Chazelas 02.06.2015, 00:42
  • 4
    @StéphaneChazelas OP действительно говорил "игнорирование символа (символов) конца строки". Так любой файл, состоящий из только \0 и \n символы (даже нуль любого) были бы соответствием. –  l0b0 02.06.2015, 01:59

Я соглашаюсь с тем, что D_Bye говорит о нахождении корня проблемы.

Так или иначе, чтобы проверить, содержит ли файл только \0 и/или \n Вы могли использовать tr:

<file tr -d '\0\n' | wc -c

Который возвращается 0 для пустого указателя/новой строки и пустых файлов.

6
27.01.2020, 19:48
  • 1
    tr -d '\0\n' решает проблему новой строки, которая затем только оставляет проблему (?) пустых файлов, перечисляемых в выводе... Это действительно обрабатывает каждый байт каждого файла, хотя (который может или не может быть проблемой), +1 –  Peter.O 20.12.2012, 15:02
  • 2
    @Peter. O: Я пропустил требование новой строки, спасибо. Это решение не очень оптимизировано и если бы оно должно работать на большом количестве данных, это было бы лучше с решением, которое идет дальше после нахождения несоответствия байтам. –  Thor 20.12.2012, 18:39
  • 3
    Это работает очень хорошо. Я мой случай я только должен был удостовериться, что исключил файлы нулевой длины.Спасибо. спасибо –  Adam Ryczkowski 20.12.2012, 21:21
  • 4
    Это будет также, однако, считать файлы с новыми строками в как являющийся "пустым". –  Chris Down 22.12.2012, 13:52
  • 5
    @ChrisDown: Я ясно дал понять текст ответа относительно того, что он делает. Не ясно, что OP хочет сделать с файлами только для новой строки. –  Thor 24.12.2012, 02:16

Я подозреваю, что те файлы редки, который является, у них нет дискового пространства выделенным им, они просто указывают размер файла (du сообщил бы 0 для них).

В этом случае, с GNU находят, Вы могли сделать (предполагающий, что никакой путь к файлу не содержит символы новой строки):

find . -type f -size +0 -printf '%b:%p\n' | grep '^0:' | cut -d: -f2-
5
27.01.2020, 19:48
  • 1
    . Я никогда не думал об этом. Я попробую. Используя du будет препятствовать царапать содержание каждого файла в файловой системе, таким образом, целая процедура не взяла бы 30 + минуты для завершения. –  Adam Ryczkowski 22.12.2012, 11:58
  • 2
    printf %b выше отчетов, что du сообщил бы) –  Stéphane Chazelas 22.12.2012, 18:19

Вот маленькая программа Python, которая может сделать это:

import sys

def only_contains_nulls(fobj, chunk_size=1024):
    first = True
    while True:
        data = fobj.read(chunk_size)
        if not data:
            if first:
                return 1  # No data
            else:
                return 0
        if data.strip("\0"):
            return 1
        first = False

if __name__ == '__main__':
    with open(sys.argv[1]) as f:
        sys.exit(only_contains_nulls(f))

И в действии:

$ printf '\0\0\0' > file
$ ./onlynulls file && echo "Only nulls" || echo "Non-null characters"
Only nulls
$ printf a >> file
$ ./onlynulls file && echo "Only nulls" || echo "Non-null characters"
Non-null characters

Можно проверить несколько файлов при помощи находки -exec, xargs, GNU parallel, и подобные программы. С другой стороны, это распечатает имена файлов, с которыми нужно иметь дело:

files=( file1 file2 )
for file in "${files[@]}"; do
    ./onlynulls "$file" || printf '%s\n' "$file"
done

Примите во внимание, что, если Вы собираетесь передать вывод этого к другой программе, имена файлов могут содержать новые строки, таким образом, необходимо разграничить его по-другому (подходяще, с \0).

Если бы у Вас есть много файлов, было бы лучше использовать опцию для параллельной обработки, так как это только читает один файл за один раз.

4
27.01.2020, 19:48

Найдите файлы, которые содержат только пустые символы '\0' и символы новой строки '\n'.
q в sed заставляет каждый поиск файла выходить непосредственно после нахождения любого ненулевого символа в строке.

find -type f -name 'file-*' |
  while IFS= read -r file ;do 
      out=$(sed -n '1=; /^\x00\+$/d; i non-null
                      ; q' "$file")
      [[ $out == "1" ]] &&  echo "$file"
  done

Сделайте тестовые файлы

> file-empty
printf '%s\n' 'line1' 'line2' 'line3'      > file-with-text           
printf '%4s\n' '' '' xx | sed 's/ /\x00/g' > file-with-text-and-nulls
printf '%4s\n' '' '' '' | sed 's/ /\x00/g' > file-with-nulls-and-newlines
printf '%4s'   '' '' '' | sed 's/ /\x00/g' > file-with-nulls-only

вывод

./file-with-nulls-and-newlines
./file-with-nulls-only
3
27.01.2020, 19:48

Python

Один файл

Определите псевдоним:

alias is_binary="python -c 'import sys; sys.exit(not b\"\x00\" in open(sys.argv[1], \"rb\").read())'"

Протестируйте:

$ is_binary /etc/hosts; echo $?
1
$ is_binary `which which`; echo $?
0

Несколько файлов

Рекурсивный поиск всех двоичных файлов:

IS_BINARY='import sys; sys.exit(not b"\x00" in open(sys.argv[1], "rb").read())'
find . -type f -exec bash -c "python -c '$IS_BINARY' {} && echo {}" \;

Чтобы найти все небинарные файлы, замените && на || .

-1
27.01.2020, 19:48

Для использования GNU sed вы можете использовать опцию -z, которая определяет строку как нулевую -завершенную строку, а также сопоставляет и удаляет пустые строки, например:

if [ "$( sed -z '/^$/d' "$file" | head -c 1 | wc -c )" -eq 0 ]; then
    echo "$file contains only NULL!"
fi

Промежуточная команда head — это просто оптимизация.

0
27.01.2020, 19:48

Этот один -лайнер является наиболее эффективным способом поиска 100% nul-файлов с помощью GNU find, xargsи grep(, предполагая, что последний построен с поддержкой PCRE):

find. -type f -size +0 -readable -print0 |
  LC_ALL=C xargs -r0 grep -LP "[^\x00]" --

Преимущества этого метода перед другими предоставленными ответами::

    В поиск включаются
  • не -разреженные файлы.
  • не -недоступные для чтения файлы не передаются в grep, что позволяет избежать Permission deniedпредупреждений.
  • grepпрекратит чтение данных из файлов после обнаружения любого не-нулевого байта (LC_ALL=C, чтобы убедиться, что каждый байт интерпретируется как символ).
  • пустые файлы (нулевые байты )не включаются в результаты.
  • меньше grepпроцессов эффективно проверяют несколько файлов.
  • пути, содержащие новую строку или начинающиеся с -, обрабатываются правильно.
  • работает на большинстве встраиваемых систем, в которых отсутствует Python/Perl.

Передача параметра -Zв grepи использование xargs -r0...позволяет выполнять дальнейшие действия над файлами со 100 % NULL (, например :очистка):

find. -type f -size +0 -readable -print0 |
  LC_ALL=C xargs -0 grep -ZLP "[^\x00]" -- |
  xargs -r0 rm --

Я также рекомендую использовать опции find-P, чтобы избежать перехода по символическим ссылкам,и -xdevчтобы избежать обхода файловых систем (например, :удаленное монтирование, деревья устройств, связывание монтирования и т.д. ).

Для игнорирования символа конца строки (s)должен работать следующий вариант (, хотя я не думаю, что это хорошая идея):

find. -type f -size +0 -readable -print0 |
  LC_ALL=C xargs -r0 grep -LP "[^\x00\r\n]" --

Собираем все воедино, включая удаление ненужных файлов (100 % символов nul / newline ), чтобы предотвратить их резервное копирование:

find -P. -xdev -type f -size +0 -readable -print0 |
  LC_ALL=C xargs -0 grep -ZLP "[^\x00\r\n]" -- |
  xargs -0 rm --

Я не рекомендую включать пустые файлы (нулевые байты ), они часто существуют для оченьспецифическихцелей .

3
27.01.2020, 19:48

Вот сочетание и сокращение ответов @Tyson и @l0b0:

# --text: do not skip non-text files
grep --recursive --files-without-match -P '[^\0]'

Если вам нужен минимальный размер файла в один байт:

# -size +0c: more than zero bytes
find -type f -size +0 -exec grep --text --files-without-match -P '[^\0]' {} +
1
21.05.2021, 14:21

Теги

Похожие вопросы