Чи є зручний спосіб класифікувати файли як «двійкові» чи «текстові»?


35

Стандартні утиліти Unix люблять grepі diffвикористовують деякі евристичні класифікації файлів як "текстові" або "двійкові". (Наприклад grep, вихід може включати рядки типу Binary file frobozz matches.)

Чи є зручний тест, який можна застосувати в zshсценарії для виконання подібної класифікації "текст / двійкові"? (Окрім чогось подібного grep '' somefile | grep -q Binary.)

(Я усвідомлюю, що будь-який подібний тест обов'язково був би евристичним, а тому недосконалим.)


10
fileє стандартною утилітою і може запускати файлову магію для визначення типів файлів, наскільки це можливо. Він може розказувати більшість текстових форматів і виконує досить гідну роботу у бінарних форматах. Якщо все, що ви намагаєтеся зробити, це з’ясувати, чи є файл текстовим чи ні, це команда, яка вас цікавить.
Bratchley

@Bratchley: деякі версії fileбуде надруковано, наприклад shell script, для деяких файлів, які я хотів би класифікувати як "текст". Чи є спосіб отримати fileдрук просто textабо binary?
kjo

1
@don_crissti Це питання стосується того, хто намагається змусити людей налагоджувати його баш сценарій. Виявлення тексту - це саме те, що повинен робити сценарій. У них виникла проблема в одній із cutкоманд.
Братчлі

1
@don_crissti Те, що є відповідь на запитання A, яке працює на запитання B, не завжди робить A дублікатом B. Поміркуйте, хто шукає способу класифікації файлів як текстових чи двійкових. Що корисніше: питання "налагодити мій сценарій", на який, мабуть, є загальна відповідь, похована серед інших відповідей, характерних для цього сценарію, або загальна "як я класифікую файли як текстові чи двійкові?"
Жил "ТАК - перестань бути злим"

1
@Gilles - залежить від того, як ти його читаєш. Я насправді розглядаю питання як типовий випадок проблеми XY: ОП хоче перевірити, чи файл є текстовим файлом - і думає, що fileвихідний файл - cutце рішення, - звичайно, є пропущений простір, який робить його невдалим, і це зробило більшість людей там звертаються до Y замість X, але коментарі та відповіді Стефана показують правильний спосіб визначити, чи файл є текстовим чи ні.
don_crissti

Відповіді:


27

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

file -b --mime-type filename | sed 's|/.*||'

24
Просто пам'ятайте, що в залежності від ваших file, що ви можете пропустити деякі текстові формати: application/xml(і аналогічно , як RSS) application/ecmascript, application/json, image/svg+xml, ... Ви повинні були б білий список тих.
Болдевін

@Boldewyn вау, приємні приклади! Тому, мабуть, краща відповідь - це просто прийняти будь-який файл, який має лише символи для друку, але якось також впорається з utf-8 та подібними проблемами кодування.
meuh

Так, це суть моєї відповіді нижче. Проблема полягає лише в тому, що це рішення має переглянути весь файл ...
Boldewyn

7
@Boldewyn В принципі, application/*типи не призначені для споживання людиною, навіть якщо вони можуть бути текстовими для полегшення розвитку та налагодження. Ось чому є і a, text/xmlі an application/xml. Тож питання, чи слід розглядати їх як текст, залежить від потреб ОП.
Тобія

3
Абоcut -d/ -f1
Стефан Шазелас

20

Іншим підходом було б використання isutf8з колекції moreutils .

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


5
Приємна пропозиція. Я щойно помітив, що надання каталогу в якості аргументу повертає 0. Я хотів би віддати перевагу хоча б 1. Але потім, сміття, сміття.
meuh

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


4
Використання -fта -tзамість довгих варіантів GNU зробить її більш портативною. Зауважте, що він викличе "двійкові" файли, які він не може відкрити. Він буде називати порожні файли "текстом".
Стефан Шазелас

Домовились. Я використовував довгі форми для спеціальної документації для людей, які не знають iconv. Але -fі, -tяк правило, краще.
Boldewyn

7

Ви можете написати скрипт, який викликає дзвінки file, і використовувати випадок справи, щоб перевірити, чи цікавлять вас справи.

Наприклад

#!/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

який я вибрав, побачивши тисячу файлів, на виході mime-типу відображається лише 6 з "текстом". Можливо, відповідність "xml" на кінці виводу типу mime може бути кориснішим, скажімо, порівнянням зі "SVG", але використання сценарію для цього повертає вас до запропонованої тут пропозиції.

Вихід fileвимагає певної настройки в будь-якому сценарії і не є на 100% надійним (його плутають декілька моїх скриптів Perl, називаючи їх "даними").

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

Відповідно до таблиці порівняння тестів файлів для оболонок, Perl, Ruby та Python , Perl має -Tпараметр, який він може використовувати для надання цієї інформації. Але в ньому перераховано порівняльну функцію zsh.

Подальше читання:


На жаль file, вихід GNU для файлів svg: SVG Scalable Vector Graphics imageне містить тексту слова. Я подумав, що цей підхід буде кращим, ніж прийнята відповідь щодо перевірки типу mime, але деякі типи все одно відсутні.
Пітер Кордес

Він все одно сумує, з мім-типом; для файлу svg xterm я отримую 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

3

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

Отже, що ви хочете зробити з цією класифікацією?

  • Якщо ви хочете вибрати ascii / binary в FTP, важливо не переносити бінарний файл як ascii (або він буде пошкоджений). Отже, ви повинні перевірити, чи є у тексті звичайні тексти, html, rtf та деякі інші. Але сумнівайтеся, виберіть бінарне. І, можливо, ви також хочете перевірити, що у файлі є лише підмножина на зразок 0x0A, 0x0D та 0x20-0x7F.
  • Якщо ви хочете перенести файл у якомусь протоколі (POP3, SMTP), вам потрібно перевірити, чи вибрати кодування в base64 або просто звичайно. У цьому випадку слід перевірити, чи є непідтримувані символи.
  • Будь-який інший випадок ... може мати будь-яке інше визначення.

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).


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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.