Команда Linux: Як "знайти" лише текстові файли?


100

Після кількох пошукових запитів від Google я придумав:

find my_folder -type f -exec grep -l "needle text" {} \; -exec file {} \; | grep text

що дуже непогано і видає непотрібні тексти, такі як інформація про тип mime. Будь-які кращі рішення? У цій же папці є багато зображень та інших двійкових файлів з великою кількістю текстових файлів, які мені потрібно шукати.

Відповіді:


184

Я знаю, що це стара нитка, але я натрапив на неї і подумав, що поділюсь своїм методом, який я знайшов дуже швидким способом використання findдля пошуку лише небінарних файлів:

find . -type f -exec grep -Iq . {} \; -print

-IВаріант Grep говорить він негайно ігнорувати виконавчі файли і .опції разом з -qзмусить його негайно відповідати текстові файли , так що йде дуже швидко. Ви можете змінити -printна "a" -print0для підключення до xargs -0чогось іншого, якщо вас турбують пробіли (спасибі за підказку, @ lucas.werkmeister!)

Також перша точка потрібна лише для певних версій BSD, findтаких як OS X, але нічого не завадить просто мати її там постійно, якщо ви хочете поставити це в псевдонімі чи щось.

EDIT : Як правильно вказав @ruslan, його -andможна опустити, оскільки він мається на увазі.


16
У Mac OS X мені потрібно змінити це на find . -type f -exec grep -Il "" {} \;.
Алек Якобсон

3
Це краще, ніж відповідь peoro, тому що 1. він насправді відповідає на питання 2. Це не дає помилкових позитивних результатів 3. це набагато ефективніше
користувач123444555621

3
Ви також можете скористатись find -type f -exec grep -Iq . {} \; -and -printтим перевагою, яке зберігає файли find; ви можете замінити -printіншим, -execякий виконується лише для текстових файлів. (Якщо ви дозволите grepнадрукувати назви файлів, ви не зможете розрізнити назви файлів із новими рядками.)
Лукас Веркермайстер,

1
@ NathanS.Watson-Haigh Це не повинно, тому що воно має відповідати текстовим файлам негайно. Чи є у вас конкретний випадок використання, яким ви можете поділитися?
crudcore

2
find . -type f -exec grep -Il . {} +набагато швидше. Недолік полягає в тому, що його не можна продовжити іншим, -execяк запропонував @ lucas.werkmeister
Геннінг


10

Чому це непогано? Якщо вам потрібно використовувати його часто, і не хочете вводити його щоразу, просто визначте для нього функцію bash:

function findTextInAsciiFiles {
    # usage: findTextInAsciiFiles DIRECTORY NEEDLE_TEXT
    find "$1" -type f -exec grep -l "$2" {} \; -exec file {} \; | grep text
}

помістіть його у свій, .bashrcа потім просто запустіть:

findTextInAsciiFiles your_folder "needle text"

коли схочеш.


EDIT, щоб відобразити редагування ОП:

якщо ви хочете вирізати інформацію про міми, ви можете просто додати ще один етап до трубопроводу, який фільтрує інформацію про міми. Це має зробити трюк, приймаючи тільки те , що передує :: cut -d':' -f1:

function findTextInAsciiFiles {
    # usage: findTextInAsciiFiles DIRECTORY NEEDLE_TEXT
    find "$1" -type f -exec grep -l "$2" {} \; -exec file {} \; | grep text | cut -d ':' -f1
}

Я не впевнений, чи "grep text" є достатньо точним, щоб отримати точно всі текстові файли - я маю на увазі, чи є текстові типи файлів, у яких в тексті опису типу mime немає "тексту"?
datasn.io

@ kavoir.com: так. З fileпосібника: "Користувачі залежать від того, щоб знати, що на всіх файлах, що читаються в каталозі, надруковано слово" текст "."
перо

2
Чи не було б трохи розумніше шукати текстові файли перед тим, як збирати, замість цього, а потім фільтрувати текстові файли?
користувач невідомий

/proc/meminfoі /proc/cpuinfoт.д. - текстові файли, але file /proc/meminfoкаже /proc/meminfo: empty. Цікаво, чи слід тестувати "порожній" на додаток до "тексту", але не впевнений, чи можуть інші типи повідомляти про "порожнє".
Timo Kähkönen

"Чому це безглуздо?" - "виводить непотрібні тексти". Ця відповідь не відповідає цьому.
користувач123444555621

4
find . -type f -print0 | xargs -0 file | grep -P text | cut -d: -f1 | xargs grep -Pil "search"

На жаль, це не економія місця. Введення цього сценарію в bash робить його трохи простіше.

Це безпечно для місця:

#!/bin/bash
#if [ ! "$1" ] ; then
    echo "Usage: $0 <search>";
    exit
fi

find . -type f -print0 \
  | xargs -0 file \
  | grep -P text \
  | cut -d: -f1 \
  | xargs -i% grep -Pil "$1" "%"

2
У вашому сценарії є кілька питань: 1. що робити, якщо названо двійковий файл text.bin? 2. Що робити, якщо ім'я файлу містить a :?
thkala

3

Ще один спосіб зробити це:

# find . |xargs file {} \; |grep "ASCII text"

Якщо ви також хочете порожні файли:

#  find . |xargs file {} \; |egrep "ASCII text|empty"

2

Як щодо цього:

$ grep -rl "needle text" my_folder | tr '\n' '\0' | xargs -r -0 file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable'

Якщо ви хочете, щоб назви файлів були без типів файлів, просто додайте фінальний sedфільтр.

$ grep -rl "needle text" my_folder | tr '\n' '\0' | xargs -r -0 file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable' | sed 's|:[^:]*$||'

Ви можете відфільтрувати непотрібні типи файлів, додавши -e 'type'до останньої grepкоманди більше опцій .

Редагувати:

Якщо ваша xargsверсія підтримує цей -dваріант, команди вище стають простішими:

$ grep -rl "needle text" my_folder | xargs -d '\n' -r file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable' | sed 's|:[^:]*$||'

дурний мене. Не помітив рекурсивного грепу. як я зрозумів, це насправді досить швидко, хоча трохи обмежено у багатьох програмах. +1 для вас.
Antti Rytsölä

2

Ось як я це зробив ...

1. зробіть невеликий сценарій, щоб перевірити, чи є файл просто текстом istext:

#!/bin/bash
[[ "$(file -bi $1)" == *"file"* ]]

2. використання find як і раніше

find . -type f -exec istext {} \; -exec grep -nHi mystring {} \;

Я думаю, ти маєш на увазі == *"text"* ]]?
користувач невідомий

Ви можете використовувати натомість оператора відповідності `= ~" текст "]]`.
користувач невідомий

2

У мене є два питання з відповіддю до хмільності:

  • У ньому перераховані лише текстові файли. Він насправді не шукає їх, як вимагається. Щоб насправді шукати, використовуйте

    find . -type f -exec grep -Iq . {} \; -and -print0 | xargs -0 grep "needle text"
    
  • Він породжує процес grep для кожного файлу, який відбувається дуже повільно. Тоді краще рішення

    find . -type f -print0 | xargs -0 grep -IZl . | xargs -0 grep "needle text"
    

    або просто

    find . -type f -print0 | xargs -0 grep -I "needle text"
    

    Це займає лише 0,2 секунди порівняно з 4 для рішення вище (2,5 ГБ даних / 7700 файлів), тобто на 20 разів швидше .

Також ніхто не цитував альтернативи Ag , Silver Silver Searcher чи ack-grep ¸ як альтернативи. Якщо один із них доступний, вони є набагато кращими альтернативами:

ag -t "needle text"    # Much faster than ack
ack -t "needle text"   # or ack-grep

Як остання примітка, остерігайтеся помилкових позитивних даних (двійкові файли, взяті як текстові файли). Я вже мав помилковий позитив, використовуючи або grep / ag / ack, тому краще перерахуйте відповідні файли спочатку перед редагуванням файлів.


1

Хоча це давнє питання, я думаю, що ця інформація нижче додасть якості відповідей тут.

Ігноруючи файли з встановленим виконуваним бітом , я просто використовую цю команду:

find . ! -perm -111

Щоб не рекурсивно входити в інші каталоги:

find . -maxdepth 1 ! -perm -111

Немає необхідності в трубах для змішування безлічі команд, просто потужна звичайна команда пошуку .

  • Відмова від відповідальності: це не саме те , що запитував ОП, оскільки він не перевіряє, чи файл є бінарним чи ні. Наприклад, він відфільтрує файли скриптів bash , які є самими текстами, але мають встановлений виконуваний біт .

Однак, я сподіваюся, що це стане в нагоді будь-кому.


0

Я роблю це так: 1) оскільки для пошуку через них занадто багато файлів (~ 30 к), я щодня генерую список текстових файлів для використання через crontab за допомогою команди нижче:

find /to/src/folder -type f -exec file {} \; | grep text | cut -d: -f1 > ~/.src_list &

2) створити функцію в .bashrc:

findex() {
    cat ~/.src_list | xargs grep "$*" 2>/dev/null
}

Тоді я можу використовувати команду нижче для пошуку:

findex "needle text"

HTH :)


0

Я віддаю перевагу xargs

find . -type f | xargs grep -I "needle text"

якщо ваші імена файлів дивні, знайдіть параметри -0:

find . -type f -print0 | xargs -0 grep -I "needle text"

0
  • приклад bash для пошуку тексту "eth0" в / etc у всіх текстових / файлах ascii

grep eth0 $ (find / etc / -type f -exec файл {} \; | egrep -i "text | ascii" | cut -d ':' -f1)


0

Ось спрощена версія з розширеним поясненням для початківців, як я, які намагаються навчитися ставити більше однієї команди в один рядок.

Якби ви виписали проблему поетапно, це виглядатиме так:

// For every file in this directory
// Check the filetype
// If it's an ASCII file, then print out the filename

Для досягнення цієї мети ми можемо використовувати три UNIX команди: find, fileі grep.

find перевірятиме кожен файл у каталозі.

fileдасть нам тип файлі. У нашому випадку ми шукаємо повернення тексту "ASCII"

grep шукатиме ключове слово "ASCII" у висновку з file

Тож як ми можемо об'єднати їх в один рядок? Існує кілька способів зробити це, але я вважаю, що робити це в порядку нашого псевдо-коду має найбільш сенс (особливо для початківця, як я).

find ./ -exec file {} ";" | grep 'ASCII'

Виглядає складно, але непогано, коли ми його розбиваємо:

find ./= перегляньте кожен файл цього каталогу. У findкоманді виводить ім'я файлу будь-якого файлу , який відповідає «висловом», або все , що приходить після того, як шлях, який в нашому випадку є поточним каталогом або./

Найголовніше, що потрібно зрозуміти, це те, що все після цього першого біта буде оцінено як Істинне, або Неправдиве. Якщо True, ім'я файлу буде надруковано. Якщо ні, то команда рухається далі.

-exec= цей прапор є опцією в команді find, яка дозволяє використовувати результат якоїсь іншої команди як вираження пошуку. Це як викликати функцію в межах функції.

file {}= команда викликається всередині find. fileКоманда повертає рядок , яка говорить вам тип файлу файлу. Регулярно, це буде виглядати наступним чином : file mytextfile.txt. У нашому випадку ми хочемо, щоб він використовував будь-який файл, який розглядається findкомандою, тому ми ставимо фігурні дужки, {}щоб діяти як порожня змінна чи параметр. Іншими словами, ми просто просимо систему вивести рядок для кожного файлу в каталозі.

";"= цього вимагає findі розділовий знак в кінці нашої -execкоманди. Щоб отримати додаткові пояснення, перегляньте посібник для "знайти", якщо вам це потрібно, запустітьman find .

| grep 'ASCII'= |- це труба. Труба приймає вихід того, що знаходиться зліва, і використовує його як вхід до того, що знаходиться праворуч. Він бере висновок findкоманди (рядок, що є файлом одного файлу) і тестує її, щоб перевірити, чи містить вона рядок 'ASCII'. Якщо це так, він повертає істину.

ЗАРАЗ вираз праворуч find ./повернеться true, коли grepкоманда поверне true. Вуаля.


0

Якщо вам цікаво знайти будь-який тип файлу за їх магічними байтами, використовуючи дивовижну fileутиліту в поєднанні з потужністю find, це може стати в нагоді:

$ # Let's make some test files
$ mkdir ASCII-finder
$ cd ASCII-finder
$ dd if=/dev/urandom of=binary.file bs=1M count=1
1+0 records in
1+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.009023 s, 116 MB/s
$ file binary.file
binary.file: data
$ echo 123 > text.txt
$ # Let the magic begin
$ find -type f -print0 | \
    xargs -0 -I @@ bash -c 'file "$@" | grep ASCII &>/dev/null && echo "file is ASCII: $@"' -- @@

Вихід:

file is ASCII: ./text.txt

Легенда: $це інтерактивна підказка оболонки, куди ми вводимо наші команди

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

Пояснення:

  • find елементи, які є файлами
  • Зробіть xargsподачу кожного елемента як рядка в одну bash команду / сценарій лайнера
  • fileперевіряє тип файлу за допомогою магічного байта, grepперевіряє, чи існує ASCII, якщо так, то після виконання &&наступної команди.
  • findдрукує результати nullрозділені, це добре, щоб уникнути назви файлів з пробілами та мета-символами.
  • xargs, використовуючи -0опцію, читає їх nullокремо, -I @@ бере кожну запис і використовує як позиційний параметр / аргументи для скрипту bash.
  • --для bashзабезпечує проведення все , що приходить після того , як аргумент , навіть якщо він починається з , -як -cякі в іншому випадку могли б бути витлумачені як варіант Баш

Якщо вам потрібно знайти інші типи, крім ASCII, просто замініть їх grep ASCIIна іншіgrep "PDF document, version 1.4"


-1
find . -type f | xargs file | grep "ASCII text" | awk -F: '{print $1}'

Використовуйте команду find, щоб перелічити всі файли, використовуйте команду файлу, щоб переконатися, що вони є текстом (не тар, ключ), і, нарешті, використовуйте команду awk для фільтрації та друку результату.


-4

Як щодо цього

 find . -type f|xargs grep "needle text"

Це не "needle text"
шукається

@Navi: приклад OP надав лише знаходження файлів, що містять"needl text"
peoro

3
@Navi: тепер він більше не шукає текстові файли: якщо "needle text"
бинарний

Чому я тебе навіть слухаю?
Наві

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