Поиск всех «недвоичных» файлов


43

Можно ли использовать findкоманду, чтобы найти все «недвоичные» файлы в каталоге? Вот проблема, которую я пытаюсь решить.

Я получил архив файлов от пользователя Windows. Этот архив содержит исходный код и файлы изображений. Наша система сборки не очень хорошо работает с файлами, имеющими оконные строки. У меня есть программа командной строки ( flip -u), которая будет переворачивать окончания строки между * nix и windows. Итак, я хотел бы сделать что-то вроде этого

find . -type f | xargs flip -u

Однако, если эта команда будет запущена для файла образа или другого двоичного медиафайла, файл будет поврежден. Я понимаю, что мог бы составить список расширений файлов и отфильтровать их, но я бы предпочел иметь что-то, что не зависит от меня, поддерживая этот список в актуальном состоянии.

Итак, есть ли способ найти все недвоичные файлы в дереве каталогов? Или я должен рассмотреть альтернативное решение?


1
Вы можете использовать fileутилиту где-нибудь в вашем скрипте / конвейере, чтобы определить, является ли файл данными или текстом
lk-

1
Что вы подразумеваете под недвоичным (все на современном компьютере является двоичным)? Я предполагаю, что вы используете отличие от старой операционной системы C / PM, в которой были текстовые и двоичные файлы. Текстовые файлы могут быть любой длины, но должны заканчиваться ctrl-z, а двоичные файлы должны быть кратны блоку 512 байт. Если это так, вы имеете в виду текстовый файл. (Я также отмечаю, что вы пишете о конце строки в недвоичных файлах, это также предполагает, что они являются текстовыми файлами) Это правильно?
Ctrl-Alt-Delor

Все файлы являются двоичными, это просто вопрос интерпретации. Вы спрашиваете, как найти текстовые файлы?
ctrl-alt-delor

@ Richard Я пришел в эпоху, когда мы называли файлы, которые должны интерпретироваться как обычный текст , а все остальные файлы (изображения, документы для обработки текста и т. д.) двоичными. Я знаю, что все это только одни и нули под капотом :)
Алан Сторм

1
Ах, я понимаю, что вы имеете в виду о моих терминах - я буду использовать двоичный текст / текст в будущем, чтобы избежать путаницы. Re: \ r \ n вещь - это, как я понимаю, это символы ASCII для возврата каретки пишущей машинки (переход к началу строки) и перевода строки (переход на одну строку вниз). Таким образом, \ r \ n - это "более точная" модель физической вещи реального мира, для которой был характер конца строки. До OS X, Mac использовали для этого всего лишь тег. Я обычно списываю все это как «произвольный выбор, сделанный в спешке, с которой мы все еще имеем дело»
Алан Сторм

Ответы:


20

Я использовал бы fileи передавал вывод в grep или awk, чтобы найти текстовые файлы, затем извлекал бы только часть имени файла fileвывода и передавал ее в xargs.

что-то типа:

file * | awk -F: '/ASCII text/ {print $1}' | xargs -d'\n' -r flip -u

Обратите внимание, что grep ищет «текст ASCII», а не просто текст - вы, вероятно, не хотите связываться с документами Rich Text, текстовыми файлами Unicode и т. Д.

Вы также можете использовать find(или что-то еще) для создания списка файлов для проверки file:

find /path/to/files -type f -exec file {} + | \
  awk -F: '/ASCII text/ {print $1}' | xargs -d'\n' -r flip -u

-d'\n'Аргумент xargs делает xargs рассматривать каждую входную линию в качестве отдельного аргумента, таким образом , питания для имен файлов с пробелами и другими проблемными символами. то есть это альтернатива тому, xargs -0когда входной источник не генерирует или не может генерировать NULL-разделенный вывод (например, параметр find's' -print0). Согласно журналу изменений, xargs получил опцию -d/ --delimiterв сентябре 2005 года, поэтому должен быть в любом не древнем дистрибутиве Linux (я не был уверен, поэтому я проверил - я просто смутно вспомнил, что это было «недавнее» дополнение).

Обратите внимание, что перевод строки является допустимым символом в именах файлов, поэтому он будет прерываться, если в именах файлов есть переводы строк. Для обычных пользователей Unix это патологически безумно, но не случайно, если файлы были созданы на компьютерах Mac или Windows.

Также обратите внимание, что fileне идеально. Это очень хорошо для определения типа данных в файле, но иногда может запутаться.

Я использовал многочисленные вариации этого метода много раз в прошлом с успехом.


1
Спасибо за это решение! По какой-то причине fileотображается, English textа не ASCII textв моей системе Solaris, поэтому я изменил эту часть соответственно. Также я заменил awk -F: '{print $1}'на аналог cut -f1 -d:.
Эндрю Чонг

3
Стоит сказать grep -Iфильтры бинарных файлов
ксенотеррацид

Поиск слова textдолжен быть достаточным. Это также подберет fileописания как ASCII Java program textили HTML document textили troff or preprocessor input text.
user1024

Мой ответ частично является ответом / улучшением этого ответа. Очень хорошее замечание по поводу поиска, ASCII textчтобы избежать путаницы RTF.
Wildcard

1
xenoterracide: ты спас мне жизнь, мужик! Просто флаг-я и БИНГО
Серхио Абреу

9

Нет. В двоичном или недвоичном файле нет ничего особенного. Вы можете использовать эвристику, например, «содержит только символы в 0x01–0x7F», но это будет вызывать текстовые файлы с двоичными файлами не-символов ASCII и текстовые файлы с незадачливыми двоичными файлами.

Теперь, когда вы проигнорировали это ...

почтовые файлы

Если он поступает от пользователя Windows в виде zip-файла, формат zip поддерживает маркировку файлов как двоичных, так и текстовых в самом архиве. Вы можете использовать -aопцию unzip, чтобы обратить на это внимание и конвертировать. Конечно, см. Первый абзац, почему это может быть не очень хорошей идеей (программа zip, возможно, догадалась неправильно, когда создавала архив).

zipinfo сообщит вам, какие файлы являются двоичными (b) или текстовыми (t) в списке zipfile.

другие файлы

Команда file проверит файл и попытается его идентифицировать. В частности, вы, вероятно, найдете его -i(выходной тип MIME) полезным; конвертировать только файлы с типом текста / *


6

Общее решение для обработки только недвоичных файлов с bashиспользованием file -b --mime-encoding:

while IFS= read -d '' -r file; do
  [[ "$(file -b --mime-encoding "$file")" = binary ]] &&
    { echo "Skipping   $file."; continue; }

  echo "Processing $file."

  # ...

done < <(find . -type f -print0)

Я связался с автором файловой утилиты, и он добавил изящный -00параметр в версии 5.26 (выпущена 2016-04-16, например, в текущих версиях Arch и Ubuntu 16.10), которая печатает сразу file\0result\0для нескольких файлов, поданных в нее, таким образом, вы можете сделать это например:

find . -type f -exec file -00 --mime-encoding {} + |
  awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}' | 

( awkЧасть состоит в том, чтобы отфильтровать каждый файл, который не является двоичным. ORSЭто разделитель вывода.)

Может также использоваться в цикле:

while IFS= read -d '' -r file; do

  echo "Processing $file."

  # ...

done < <(find . -type f -exec file -00 --mime-encoding {} + |
  awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}')

Основываясь на этом и предыдущем, я создал небольшой bashскрипт для фильтрации двоичных файлов, который использует новый метод с использованием -00параметра fileв более новых его версиях и возвращается к предыдущему методу в более старых версиях:

#!/bin/bash

# Expects files as arguments and returns the ones that do
# not appear to be binary files as a zero-separated list.
#
# USAGE:
#   filter_binary_files.sh [FILES...]
#
# EXAMPLE:
#   find . -type f -mtime +5 -exec ./filter_binary_files.sh {} + | xargs -0 ...
# 

[[ $# -eq 0 ]] && exit

if [[ "$(file -v)" =~ file-([1-9][0-9]|[6-9]|5\.([3-9][0-9]|2[6-9])) ]]; then
  file -00 --mime-encoding -- "$@" |
    awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}'
else
  for f do
    [[ "$(file -b --mime-encoding -- "$f")" != binary ]] &&
      printf '%s\0' "$f"
  done
fi

Или здесь более POSIX-у, но он требует поддержки sort -V:

#!/bin/sh

# Expects files as arguments and returns the ones that do
# not appear to be binary files as a zero-separated list.
#
# USAGE:
#   filter_binary_files.sh [FILES...]
#
# EXAMPLE:
#   find . -type f -mtime +5 -exec ./filter_binary_files.sh {} + | xargs -0 ...
# 

[ $# -eq 0 ] && exit

if [ "$(printf '%s\n' 'file-5.26' "$(file -v | head -1)" | sort -V)" = \
    'file-5.26' ]; then
  file -00 --mime-encoding -- "$@" |
    awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}'
else
  for f do
    [ "$(file -b --mime-encoding -- "$f")" != binary ] &&
      printf '%s\0' "$f"
  done
fi

6

Принятый ответ не нашел их всех для меня. Вот пример использования grep -Iдля игнорирования двоичных файлов и игнорирования всех скрытых файлов ...

find . -type f -not -path '*/\.*' -exec grep -Il '.' {} \; | xargs -L 1 echo 

Вот он используется в практическом применении: dos2unix

https://unix.stackexchange.com/a/365679/112190


4

Ответ Кас хороший, но он предполагает вменяемые имена файлов; в частности предполагается, что имена файлов не будут содержать переводы строки.

Здесь нет веских оснований для такого предположения, поскольку довольно просто (и, на мой взгляд, более чисто) правильно обрабатывать этот случай:

find . -type f -exec sh -c 'file "$1" | grep -q "ASCII text"' sh {} \; -exec flip -u {} \;

Команда использует findтолько функции, указанные в POSIX . Использование -execдля запуска произвольных команд в качестве логических тестов является простым, надежным (корректно обрабатывает нечетные имена файлов) и более переносимым, чем -print0.

Фактически, все части команды определены POSIX, кроме flip.

Обратите внимание, что fileэто не гарантирует точности результатов, которые он возвращает. Однако на практике поиск текста «ASCII» в его выводе достаточно надежен.

(Возможно, он может пропустить некоторые текстовые файлы, но очень маловероятно, чтобы неправильно идентифицировать двоичный файл как «текст ASCII» и исказить его - поэтому мы ошибаемся из-за осторожности.)


Файл без аргументов callsможет быть довольно медленным, например, для видео он расскажет вам все о кодировке.
phk

Также вы предполагаете, что файл не начинается с -.
phk

И я не вижу причин, по которым вы бы не просто сделали один вызов file, он может принимать несколько файлов в качестве аргументов.
phk

@phk, чтобы ответить на ваши комментарии: (1) хорошо знать потенциальную медлительность, но я не вижу способа POSIX предотвратить это; (2) я делаю нулевые предположения об именах файлов, так как findкоманда будет иметь префикс ./перед любым именем файла, передаваемым команде оболочки; (3) Использование grepв качестве теста для fileвывода одной команды за раз - единственный способ POSIX, который я вижу, чтобы гарантировать правильную обработку имен файлов, которые могут содержать переводы строки.
Wildcard

Я просмотрел ваше окончательное решение "POSIX-y" и думаю, что оно умное, но вы предполагаете, что fileподдерживает --mime-encodingфлаг и --разделитель, ни одно из которых не гарантируется POSIX .
Wildcard

2
find . -type f -exec grep -I -q . {} \; -print

Это найдет все обычные файлы ( -type f) в текущем каталоге (или ниже), которые grepсчитают , что они не пустые и не двоичные.

Используется grep -Iдля различения двоичных и недвоичных файлов. -IФлаг и приведет grepк выходу со статусом ненулевым , когда он обнаруживает , что файл является двоичным. «Двоичный» файл - это, в соответствии с grepфайлом, который содержит символ вне диапазона ASCII для печати.

-qВариант grepзаставит его выйти со статусом нулевого выхода , если данный шаблон найден, без выделения каких - либо данных. Шаблон, который мы используем, представляет собой одну точку, которая будет соответствовать любому символу.

Если файл не двоичный и содержит хотя бы один символ, печатается имя файла.

Если вы чувствуете себя смелым, вы также можете подключиться flip -uк нему:

find . -type f -exec grep -I -q . {} \; -print -exec flip -u {} \;

1

Попробуй это :

find . -type f -print0 | xargs -0 -r grep -Z -L -U '[^         -~]' | xargs -0 -r flip -u

Если аргумент grep '[^ -~]'IS '[^<tab><space>-~]'.

Если вы вводите его в командной строке оболочки, введите Ctrl+ Vперед Tab. В редакторе не должно быть проблем.

  • '[^<tab><space>-~]'будет соответствовать любому символу, который не является текстом ASCII (возврат каретки игнорируется grep).
  • -L будет печатать только имя файла файлов, которые не совпадают
  • -Zвыведет имена файлов, разделенные нулевым символом (для xargs -0)

Стоит отметить, что с Perl-подобным Regex grep -P(если доступно) \tдоступно. В качестве альтернативы можно использовать перевод локали, если оболочка его поддерживает: $'\t'( bashи zshделать).
phk

1

Альтернативное решение:

Команда dos2unix преобразует окончания строк из Windows CRLF в Unix LF и автоматически пропускает двоичные файлы. Я применяю это рекурсивно, используя:

find . -type f -exec dos2unix {} \;

Поскольку в dos2unixкачестве аргумента можно использовать несколько имен файлов, это гораздо эффективнееfind . -type f -exec dos2unix {} +
Anthon

0

sudo find / (-type f -and -path '* / git / *' -iname 'README') -exec grep -liI '100644 \ | 100755' {} \; -exec flip -u {} \;

i. (-type f -and -path '* / git / *' -iname 'README'): поиск файлов по пути, содержащему имя git и файл с именем README. Если вы знаете какую-либо конкретную папку и имя файла для поиска, будет полезно.

Команда ii.-exec запускает команду для имени файла, сгенерированного командой find

III. \; указывает на конец команды

iv. {} - это вывод файла / имя_фолдера, найденного в предыдущем поиске поиска

v. Несколько команд могут быть запущены впоследствии. Добавив -exec "команда" \; например, с помощью -exec flip -u \;

vii.grep

1.-l lists the name of the file
2.-I searches only non-binary files
3.-q quiet output
4.'100644\|100755' searches for either 100644 or 100755 within the file found. if found it then runs flip -u. \| is the or operator for grep. 

Вы можете клонировать этот тестовый каталог и попробовать его: https://github.com/alphaCTzo7G/stackexchange/tree/master/linux/findSolution204092017

более подробный ответ здесь: https://github.com/alphaCTzo7G/stackexchange/blob/master/linux/findSolution204092017/README.md

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.