Есть ли удобный способ классифицировать файлы как «двоичные» или «текстовые»?

35

Стандартный Unix утилита , как grepи diffиспользовать некоторые эвристики для классификации файлов как «текст» или «двоичный». (Например grep, вывод может включать строки вроде Binary file frobozz matches.)

Есть ли удобный тест, который можно применить в zshсценарии для выполнения аналогичной «текстовой / двоичной» классификации? (Кроме чего-то вроде grep '' somefile | grep -q Binary.)

(Я понимаю, что любой такой тест обязательно будет эвристическим и, следовательно, несовершенным.)

KJo
источник
10
fileэто стандартная утилита, которая может использовать магию файлов для определения типов файлов в меру своих возможностей. Он может распознавать большинство текстовых форматов и довольно неплохо справляется с двоичными форматами. Если все, что вы пытаетесь сделать, это выяснить, является ли файл текстовым или нет, это команда, которая вас интересует.
Братчли
@Bratchley: некоторые версии fileбудут напечатаны, например shell script, для некоторых файлов, которые я хотел бы классифицировать как «текст». Есть ли способ получить fileпросто напечатать textили binary?
kjo
1
@don_crissti Этот вопрос о ком-то, кто пытается заставить людей отлаживать его bash-скрипт. Обнаружение текста - это то, что скрипт должен делать. В итоге у них возникла проблема в одной из cutкоманд.
Братчли
1
@don_crissti Тот факт, что есть ответ на вопрос A, который подходит для вопроса B, не всегда делает A дубликатом B. Рассмотрим кого-то, кто ищет способ классифицировать файлы как текстовые или двоичные. Что является более полезным: вопрос «отладить мой сценарий», который содержит общий ответ, скрытый среди других ответов, относящихся к этому сценарию, или общий вопрос «как классифицировать поля как текстовые или двоичные?»?
Жиль "ТАК - перестань быть злым"
1
@ Жиль - зависит от того, как ты это читаешь. Я на самом деле рассматриваю этот вопрос как типичный случай проблемы XY: OP хочет проверить, является ли файл текстовым файлом - и думает, что fileвывод данных в трубопровод cut- это решение - конечно, есть пропущенное место, которое приводит к сбою и большинство людей там обращаются к Y вместо X, но комментарии и ответ Стефана показывают правильный способ определить, является ли файл текстовым или нет.
don_crissti

Ответы:

27

Если вы запрашиваете fileтолько MIME-тип, вы получите много разных, таких как text/x-shellscriptи application/x-executableт. Д., Но я думаю, что если вы просто проверите «текстовую» часть, вы должны получить хорошие результаты. Например ( -bбез имени файла в выводе):

file -b --mime-type filename | sed 's|/.*||'
meuh
источник
24
Просто помните, что в зависимости от ваших file, что вы можете пропустить некоторые текстовые форматы: application/xml(и аналогично , как RSS) application/ecmascript, application/json, image/svg+xml, ... Вы должны были бы белый список тех.
Болдевин
@Boldewyn Вау, хорошие примеры! Поэтому, вероятно, лучшим ответом будет просто принять любой файл, который имеет только печатаемые символы, но каким-то образом также справляется с utf-8 и подобными проблемами кодирования.
Мех
Да, это суть моего ответа ниже. Единственная проблема заключается в том, что это решение должно смотреть на весь файл ...
Болдевин
7
@Boldewyn В принципе, application/*типы не предназначены для потребления человеком, даже если они могут быть основаны на тексте для облегчения разработки и отладки. Вот почему есть и a text/xmlи an application/xml. Поэтому вопрос о том, следует ли рассматривать их как текст, зависит от потребностей ФП.
Тобия
3
Илиcut -d/ -f1
Стефан Шазелас
20

Другой подход заключается в использовании isutf8из коллекции moreutils .

Он завершается с 0, если файл является допустимым UTF-8 или ASCII, или с короткими замыканиями, печатает сообщение об ошибке (тишина с -q) и завершается с 1 в противном случае.

Бродить наута
источник
5
Хорошее предложение. Я только что заметил, что если указать каталог с аргументом arg, он вернет 0. Я бы предпочел 1, по крайней мере. Но потом мусор внутри, мусор вне.
Мех
13

Если вам нравится эвристика, используемая GNU grep, вы можете использовать ее:

isbinary() {
  LC_MESSAGES=C grep -Hm1 '^' < "${1-$REPLY}" | grep -q '^Binary'
}

Он ищет байты NUL в первом буфере, считанном из файла (несколько килобайт для обычного файла, но может быть намного меньше для канала или сокета или некоторых других устройств /dev/random). В локалях UTF-8 он также помечает байтовые последовательности, которые не образуют допустимых символов UTF-8. Предполагается, что LC_ALLне установлен на что-то, где язык не является английским.

${1-$REPLY}Форма позволяет использовать его в качестве zshГлоб классификатора:

ls -ld -- *(.+isbinary)

перечислил бы двоичные файлы.

Стефан Шазелас
источник
7

Вы можете попытаться определить, iconvможете ли вы прочитать файл. Это менее эффективно, чем file(который просто читает пару байтов с начала), но даст вам более надежные результаты:

ENCODING=utf-8
if iconv --from-code="$ENCODING" --to-code="$ENCODING" your_file.ext > /dev/null 2>&1; then
    echo text
else
    echo binary
fi

Это в iconvосновном делает невозможным, но если он сталкивается с недопустимыми данными (недопустимый UTF-8 в этом примере), он прекратит работу и завершит работу.

Boldewyn
источник
4
Использование длинных опций -fи -tвместо GNU сделает его более переносимым. Обратите внимание, что он будет называть «двоичные» файлы, которые он не может открыть. Это будет называть пустые файлы «текст».
Стефан Шазелас
Согласовано. Я использовал длинные формы для специальной документации, для людей, которые не знают iconv. Но -fи -tобычно лучше.
Болдевин
7

Вы можете написать скрипт, который вызывает file, и использовать оператор case для проверки интересующих вас случаев.

Например

#!/bin/sh
case $(file "$1") in
(*script*|*\ text|*\ text\ *)
    echo text
    ;;
(*)
    echo binary
    ;;
esac

хотя, конечно, может быть много особых случаев, которые представляют интерес. Просто проверяя stringsкопию libmagic, я вижу около 200 случаев, например,

Konqueror cookie text
Korn shell script text executable
LaTeX 2e document text
LaTeX document text
Linux Software Map entry text
Linux Software Map entry text (new format)
Linux kernel symbol map text
Lisp/Scheme program text
Lua script text executable
LyX document text
M3U playlist text
M4 macro processor script text

Некоторые используют строку «текст» как часть другого типа, например,

SoftQuad troff Context intermediate   
SoftQuad troff Context intermediate for AT&T 495 laser printer
SoftQuad troff Context intermediate for HP LaserJet

Точно так же scriptможет быть частью слова, но я не вижу проблем в этом случае. Но скрипт должен проверять "text"как слово , а не подстроку .

Напоминаем, что в fileвыводе не используется точное описание, которое всегда содержит «скрипт» или «текст». Особые случаи - это то, что нужно учитывать. Продолжение прокомментировало, что --mime-typeработает, пока этот подход не будет, для .svgфайлов. Тем не менее, в тесте я вижу эти результаты для SVG-файлов:

$ ls -l *.svg
-r--r--r-- 1 tom users  6679 Jul 26  2012 pumpkin_48x48.svg
-r--r--r-- 1 tom users 17372 Jul 30  2012 sink_48x48.svg
-r--r--r-- 1 tom users  5929 Jul 25  2012 vile_48x48.svg
-r--r--r-- 1 tom users  3553 Jul 28  2012 vile-mini.svg
$ file *.svg
pumpkin_48x48.svg: SVG Scalable Vector Graphics image
sink_48x48.svg:    SVG Scalable Vector Graphics image
vile-mini.svg:     SVG Scalable Vector Graphics image
vile_48x48.svg:    SVG Scalable Vector Graphics image
$ file --mime-type *.svg
pumpkin_48x48.svg: image/svg+xml
sink_48x48.svg:    image/svg+xml
vile-mini.svg:     image/svg+xml
vile_48x48.svg:    image/svg+xml

который я выбрал, увидев тысячу файлов, показывающих только 6 с текстом в выводе mime-типа. Возможно, сопоставление «xml» в конце вывода mime-типа может быть более полезным, скажем, чем сопоставление «SVG», но использование сценария для выполнения этого возвращает вас к предложению, сделанному здесь.

Вывод fileтребует некоторой настройки в любом сценарии и не является на 100% надежным (его смущают несколько моих сценариев Perl, называющих их «данные»).

Существует более одной реализации file. Наиболее часто используемая выполняет свою работу libmagic, которую можно использовать из разных программ (возможно, не напрямую zsh, хотя pythonможно).

Согласно таблице сравнения тестов файлов для оболочки, Perl, Ruby и Python , в Perl есть -Tопция, которую он может использовать для предоставления этой информации. Но это не перечисляет сопоставимую функцию для zsh.

Дальнейшее чтение:

Томас Дики
источник
К сожалению file, вывод GNU для файлов SVG: SVG Scalable Vector Graphics imageне содержит слова text. Я думал, что этот подход будет лучше, чем принятый ответ проверки типа mime, но он все еще пропускает некоторые типы.
Питер Кордес
Это все еще отсутствует, с типом пантомимы; для файла xterm svg я получаю image/svg+xml. На самом деле - только что проверил 1000-файл то же самое, только 6 вышли как «текст» в соответствии только с MIME-тип. Я буду придерживаться сценария, который, по крайней мере, можно заставить работать по мере необходимости.
Томас Дики
3

fileимеет опцию, --mime-encodingкоторая пытается определить кодировку файла.

 $file --mime-encoding Documents/poster2.pdf 
Documents/poster2.pdf: binary
 $file --mime-encoding projects/linux/history-torvalds/Makefile 
projects/linux/history-torvalds/Makefile: us-ascii
 $file --mime-encoding graphe.tex 
Dgraphe.tex: us-ascii
 $file --mime-encoding software.tex 
software.tex: utf-8

Вы можете использовать, file --mime-encoding | grep binaryчтобы определить, является ли файл двоичным файлом. Он работает надежно, хотя может быть запутан одним недопустимым символом в длинном текстовом файле.

Например, я псевдоним catследующего сценария оболочки, чтобы избежать разрушения моего терминала, случайно открыв двоичный файл:

#! /bin/sh -

[ ! -t 1 ] && exec /bin/cat "$@"
for i
do
    if file --mime-encoding -- "$i" | grep -q binary
    then
        hexdump -C -- "$i"
    else
        /bin/cat -- "$i"
    fi
done
lgeorget
источник
3

Категории являются произвольными. Прежде чем ответить, как сделать классификацию, нужно (строгое) определение. Чтобы иметь определение, вам нужна цель .

Итак, что вы хотите сделать с этой классификацией?

  • Если вы хотите выбрать ascii / binary в FTP, важно не передавать двоичный файл как ascii (или он будет поврежден). Таким образом, вы должны проверить, является ли файл обычными текстами, html, rtf и некоторыми другими. Но в сомнении выберите бинарный файл. И, возможно, вы также хотите проверить, что файл имеет только подмножество, например 0x0A, 0x0D и 0x20-0x7F.
  • Если вы хотите передать файл по какому-либо протоколу (POP3, SMTP), вам нужно проверить, выбрать ли кодирование в base64 или просто. В этом случае вам следует проверить, есть ли неподдерживаемые символы.
  • Любой другой случай ... может иметь любое другое определение.
ESL
источник
3
perl -e'chomp(my$f=<>);print "binary$/" if -B $f;print "text$/" if -T _'

сделаю это. Смотрите документацию для -Bи-T (ищите на этой странице строку The -T and -B switches work as follows).

msh210
источник
perl -le 'print -B $ARGV[0] ? "binary" : "text"' --может быть понятнее Или дажеperl -le 'print -B $_ ? "binary" : "text", @ARGV > 1 ? "\t$_" : "" for @ARGV' --
jrw32982 поддерживает Монику
1

Я внес вклад в https://github.com/audreyr/binaryornot. У него нет оболочки для командной строки (пока), но это простая библиотека Python, которую достаточно просто вызвать даже из CLI. Он использует довольно эффективную эвристику, чтобы определить, является ли файл текстовым или двоичным.

Филипп Омбредан
источник
1

Мне сейчас этот ответ немного староват, но я думаю, что мой друг научил меня хорошему «взлому» сделать это.

Вы используете diffкоманду и сверяете свой файл с тестовым текстовым файлом:

$ diff filetocheck testfile.txt

Теперь, если filetocheckэто бинарный файл, вывод будет:

Binary files filetocheck and testfile.txt differ

Таким образом, вы можете использовать diffкоманду и, например, написать функцию, которая выполняет проверку в скрипте.

user3019105
источник