Команда Linux: Как «найти» только текстовые файлы?

100

После нескольких поисков в Google я пришел к следующему:

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

что очень неудобно и выводит ненужные тексты, такие как информация о типе MIME. Есть лучшие решения? У меня много изображений и других двоичных файлов в одной папке с большим количеством текстовых файлов, которые мне нужно найти.

datasn.io
источник

Ответы:

184

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

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

Параметр -Igrep указывает ему немедленно игнорировать двоичные файлы, а .параметр вместе с параметром -qзаставит его немедленно сопоставить текстовые файлы, поэтому он выполняется очень быстро. Если вас беспокоят пробелы, вы можете заменить его -printна a -print0для подключения к трубопроводу xargs -0или чего-то еще (спасибо за подсказку, @ lucas.werkmeister!)

Кроме того, первая точка необходима только для определенных версий BSD, findнапример, в OS X, но это не повредит, если вы хотите поместить ее в псевдоним или что-то в этом роде.

РЕДАКТИРОВАТЬ : Как правильно указал @ruslan, -andможно опустить, поскольку это подразумевается.

Crudcore
источник
16
В Mac OS X мне нужно изменить это на find . -type f -exec grep -Il "" {} \;.
Алек Джейкобсон
3
Это лучше, чем ответ peoro, потому что 1. он действительно отвечает на вопрос 2. Он не дает ложных срабатываний 3. он намного эффективнее
user123444555621
3
Вы также можете использовать find -type f -exec grep -Iq . {} \; -and -printwhich имеет то преимущество, что он хранит файлы find; вы можете заменить его -printдругим, -execкоторый запускается только для текстовых файлов. (Если вы разрешите grepпечатать имена файлов, вы не сможете различать имена файлов с символами новой строки в них.)
Лукас Веркмайстер,
1
@ NathanS.Watson-Haigh Этого не должно быть, потому что текстовые файлы должны совпадать немедленно. У вас есть конкретный пример использования, которым вы можете поделиться?
crudcore
2
find . -type f -exec grep -Il . {} +намного быстрее. Недостатком является то, что он не может быть продлен другим, -execкак предложил @ lucas.werkmeister
Henning
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"

когда угодно.


ИЗМЕНИТЬ, чтобы отразить редактирование OP:

если вы хотите вырезать MIME-информацию, вы можете просто добавить в конвейер еще один этап, который фильтрует MIME-информацию. Это должно помочь, если взять только то, что было раньше ::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» достаточно точен, чтобы получить точно все текстовые файлы - я имею в виду, есть ли какие-либо типы текстовых файлов, у которых нет «text» в строке описания его типа mime?
datasn.io
@ kavoir.com: да. Из fileруководства: «Пользователи должны знать, что все читаемые файлы в каталоге имеют напечатанное слово« текст »».
peoro
2
Разве не было бы умнее искать текстовые файлы перед grep, вместо того, чтобы использовать grep и затем фильтровать текстовые файлы?
user unknown
/proc/meminfoи /proc/cpuinfoт.д. являются текстовыми файлами, но file /proc/meminfoговорит /proc/meminfo: empty. Мне интересно, нужно ли тестировать «пустой» в дополнение к «тексту», но не уверен, могут ли другие типы сообщать «пустой».
Timo Kähkönen
"Почему это неудобно?" - «выводит ненужные тексты». Этот ответ не говорит об этом.
user123444555621
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. Что делать, если имя файла содержит :?
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|:[^:]*$||'
Thkala
источник
я такой глупый. Не заметил рекурсивный grep. как я понял, на самом деле он довольно быстрый, хотя и немного ограничен во многих приложениях. +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"* ]]?
user unknown
Вместо этого вы можете использовать оператор сопоставления `= ~" text "]]`.
user unknown
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 Searcher или ack-grep в качестве альтернатив. Если один из них доступен, это гораздо лучшая альтернатива:

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

И последнее примечание: остерегайтесь ложных срабатываний (двоичные файлы принимаются как текстовые). У меня уже было ложное срабатывание при использовании grep / ag / ack, поэтому лучше сначала перечислить совпадающие файлы, прежде чем редактировать файлы.

Fuujuhi
источник
1

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

При игнорировании файлов с установленным исполняемым битом я просто использую эту команду:

find . ! -perm -111

Чтобы он не рекурсивно заходил в другие каталоги:

find . -maxdepth 1 ! -perm -111

Нет необходимости использовать каналы для смешивания большого количества команд, только мощная простая команда find .

  • Отказ от ответственности: это не совсем то , что спрашивал OP, потому что он не проверяет, является ли файл двоичным или нет. Он, например, отфильтрует файлы сценариев bash , которые сами по себе являются текстовыми, но имеют установленный исполняемый бит .

Тем не менее, я надеюсь, что это будет полезно для всех.

Д-р Беко
источник
0

Я делаю это следующим образом: 1) поскольку файлов слишком много (~ 30k) для поиска, я ежедневно генерирую список текстовых файлов для использования через 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 во всех файлах text / ascii

grep eth0 $ (найти / etc / -type f -exec file {} \; | 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 или False. Если True, имя файла будет распечатано. Если нет, то команда переходит к следующему.

-exec= этот флаг - опция в команде find, которая позволяет нам использовать результат какой-либо другой команды в качестве выражения поиска. Это похоже на вызов функции внутри функции.

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

";"= это требуется для findзнака препинания в конце нашей -execкоманды. См. Руководство по поиску для получения дополнительных объяснений, если вам это нужно, запустив man find.

| grep 'ASCII'= |это труба. Pipe принимает вывод того, что находится слева, и использует его как ввод для всего, что находится справа. Он берет выходные данные findкоманды (строку, которая является типом файла одного файла) и проверяет его, чтобы увидеть, содержит ли он строку 'ASCII'. Если да, возвращается истина.

СЕЙЧАС выражение справа find ./вернет истину, когда grepкоманда вернет истину. Вуаля.

Mepler
источник
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.
  • --for bashгарантирует, что все, что идет после него, является аргументом, даже если оно начинается с -like, -cкоторое в противном случае могло бы быть интерпретировано как параметр bash

Если вам нужно найти типы, отличные от ASCII, просто замените grep ASCIIна другой тип, напримерgrep "PDF document, version 1.4"

sdkks
источник
-1
find . -type f | xargs file | grep "ASCII text" | awk -F: '{print $1}'

Используйте команду find, чтобы вывести список всех файлов, используйте команду file, чтобы убедиться, что они текстовые (не tar, key), наконец, используйте команду awk для фильтрации и печати результата.

Рой Зенг
источник
-4

Как насчет этого

 find . -type f|xargs grep "needle text"
Navi
источник
Это не ищет"needle text"
peoro
@Navi: приведенный пример OP находит только файлы, содержащие"needl text"
peoro
3
@Navi: теперь он больше не ищет текстовые файлы: если бинарный файл содержит, "needle text"он будет найден
peoro
Почему я вообще тебя слушаю?
Navi
1
@Navi: ваш однострочный файл не проверяет типы файлов, а также имеет серьезные проблемы с пробелами в именах файлов ...
thkala