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

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

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

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

Ответ 1

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

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

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

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

Ответ 2

Почему это неудобно? Если вам нужно часто использовать его и не хотите вводить его каждый раз, просто определяйте для него функцию 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, чтобы отразить редактирование OP:

Если вы хотите вырезать информацию о миме, вы можете просто добавить еще один этап в конвейер, который отфильтровывает информацию о миме. Это должно сделать трюк, взяв только то, что предшествует :: 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
}

Ответ 4

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

Это, к сожалению, не сохранение пространства. Помещение этого в bash script делает его немного проще.

Это безопасно:

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

Ответ 5

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

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

EDIT:

Если ваша версия xargs поддерживает параметр -d, команды выше упрощаются:

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

Ответ 6

Вот как я это сделал...

1. сделайте небольшой script, чтобы проверить, является ли файл простым текстом ISTEXT:

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

2. использовать поиск по-прежнему

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

Ответ 7

У меня есть две проблемы с ответом гистовости:

  • Он отображает только текстовые файлы. На самом деле они не ищут их как просил. Чтобы выполнить поиск, используйте

    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"
    

    Это занимает всего 0,2 с по сравнению с 4s для решения выше (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, поэтому лучше сначала сопоставить сопоставленные файлы перед редактированием файлов.

Ответ 8

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

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

find . ! -perm -111

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

find . -maxdepth 1 ! -perm -111

Не нужно, чтобы каналы смешивали множество команд, только мощная команда поиска.

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

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

Ответ 9

Другой способ сделать это:

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

Если вам нужны и пустые файлы:

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

Ответ 10

Я делаю так: 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"

НТН:)

Ответ 11

Я предпочитаю xargs

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

если ваши имена файлов выглядят странно, используя опции -0:

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

Ответ 12

  • bash пример для текста "eth0" в файле /etc во всех файлах text/ascii

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

Ответ 13

Вот упрощенная версия с расширенным объяснением для начинающих, таких как я, которые пытаются научиться добавлять более одной команды в одну строку.

Если вы должны были выписать проблему пошагово, это будет выглядеть так:

// For every file in this directory
// Check the filetype
// If it 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. Подробнее см. В руководстве для "find", если вам это нужно, запустив man find.

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

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

Ответ 14

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

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