Пошук усіх "небінарних" файлів


43

Чи можна за допомогою findкоманди знайти всі "небінарні" файли в каталозі? Ось проблема, яку я намагаюся вирішити.

Я отримав архів файлів від користувача Windows. Цей архів містить вихідний код та файли зображень. Наша система побудови не грає добре з файлами, які мають закінчення рядків 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 використовували для цього лише a. Зазвичай я записую це як "довільний вибір, зроблений у поспіху, з яким ми все ще маємо справу"
Алан Шторм

Відповіді:


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 вихід (наприклад, опція finds -print0). Відповідно до журналу змін, xargs отримав -d/ --delimiteroption у вересні 2005 року, тому він повинен бути у будь-якому недавньому дистрибутиві Linux (я не був впевнений, тому я перевірив - я просто невиразно пам’ятав, що це «недавнє» доповнення).

Зауважте, що передача рядків є дійсним символом у назви файлів, тому це порушиться, якщо будь-які назви файлів мають в них стрічкові канали. Для типових користувачів Unix це патологічно божевільно, але це не чутно, якщо файли виникли на комп'ютерах Mac чи Windows.

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

Я багато разів використовував численні варіанти цього методу в минулому з успіхом.


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

3
варто сказати grep -Iфільтри бінарні файли
xenoterracide

Шукати слова textповинно бути достатньо. Це буде також підібрати fileопису , як ASCII Java program textабо HTML document textабо troff or preprocessor input text.
користувач1024

Моя відповідь частково є відповіддю / покращенням на цю відповідь. Дуже хороший момент щодо прихватування, ASCII textщоб уникнути псування RTF.
Wildcard

1
ксенотерацид: Ти врятував мені життя людини! Тільки прапор -Я і БІНГО
Серхіо Абреу

9

Ні. Немає нічого особливого у двійковому чи не бінарному файлі. Ви можете використовувати евристику на кшталт "містить лише символи в 0x01–0x7F", але це буде викликати текстові файли з двійковими файлами, що не належать до ASCII, та текстовими файлами невдачливих бінарних файлів.

Тепер, як тільки ви проігнорували це ...

поштові файли

Якщо він надходить від вашого користувача Windows у вигляді поштового файлу, формат zip підтримує маркування файлів як двійкових чи текстових у самому архіві. Ви можете використовувати -aопцію unzip, щоб звернути на це увагу та перетворити. Звичайно, дивіться перший абзац, чому це може бути не дуже хорошою ідеєю (програма zip, можливо, здогадалася неправильно, коли створила архів).

zipinfo розповість, які файли є двійковими (b) або текстовими (t) у своєму списку zipfile.

інші файли

Команда файлу перегляне файл і спробує його ідентифікувати. Зокрема, ви, ймовірно, знайдете -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-y, але він потребує підтримки для 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

Прийнята відповідь не знайшла для мене всіх. Ось приклад використання грепів -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виведенні команди одночасно є єдиним способом, який я бачу, щоб гарантувати правильну обробку імен файлів, які можуть містити нові рядки.
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 '[^ -~]'є '[^<tab><space>-~]'.

Якщо ви введете його в командному рядку оболонки, введіть Ctrl+ Vраніше Tab. У редакторі не повинно виникнути проблем.

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

Варто відзначити, що Perge-подібний 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 {} +
Антон

0

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

i. (-тип f -and -ath '* / 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.