У меня есть строка типа: "thisissometext"
. Я хочу найти все текстовые файлы внутри заданного каталога (рекурсивно), содержащие эту строку или любые ее вариации с пробелами и/или символами новой строки в середине. Например, текстовый файл, содержащий "this is sometext"
, или "this\n issometext"
, "this\n isso metext"
должен отображаться в результатах поиска. Как это сделать?
решение1
В более новых версиях GNU grep
(где есть такая -z
возможность) можно использовать следующую однострочную команду:
find . -type f -exec grep -lz 'this[[:space:]]*is[[:space:]]*some[[:space:]]*text' {} +
Учитывая, что пробелы могут быть только между словами.
Если вы просто хотите рекурсивно искать все файлы, начиная с текущего каталога, вам не нужно find
, вы можете просто использовать grep -r
(рекурсивный). find
может использоваться для выборочного поиска файлов, например, для выбора файлов из какого каталога исключить. Итак, просто:
grep -rlz 'this[[:space:]]*is[[:space:]]*some[[:space:]]*text' .
Главный трюк здесь в том
-z
, что он будет обрабатывать каждую строку входного потока, заканчивающуюся ASCII NUL, а не новой строкой, в результате чего мы можем сопоставлять новые строки, используя обычные методы.[[:space:]]
Шаблон класса символов указывает на любые пробельные символы, включая пробел, табуляцию, CR, LF и т. д. Таким образом, мы можем использовать его для сопоставления всех пробельных символов, которые могут встречаться между словами.grep -l
напечатает только имена файлов, которые имеют любой из желаемых шаблонов. Если вы хотите напечатать также совпадения, используйте-H
вместо-l
.
С другой стороны, если пробелы могут появляться в любых местах, а не только в словах, это потеряет свой привлекательный вид:
grep -rlz
't[[:space:]]*h[[:space:]]*i[[:space:]]*s[[:space:]]*i[[:space:]]*\
s[[:space:]]*s[[:space:]]*o[[:space:]]*m[[:space:]]*e[[:space:]]*\
t[[:space:]]*e[[:space:]]*x[[:space:]]*t' .
С -P
опцией (PCRE) вы можете заменить [[:space:]]
на \s
(это будет выглядеть намного лучше):
grep -rlzP 't\s*h\s*i\s*s\s*i\s*s\s*s\s*o\s*m\s*e\s*\
t\s*e\s*x\s*t' .
Лучшим вариантом было бы воспользоваться предложением @steeldriver, чтобы sed
сгенерировать шаблон для нас:
grep -rlzP "$(sed 's/./\\s*&/2g' <<< "thisissometext")" .
решение2
Вы можете удалить все пробелы и выполнить grep:
tr -d '[[:space:]]' < foo | grep thisissometext
Продление:
find . -type f -exec bash -c 'for i; do tr -d "[[:space:]]" < "$i" | grep -q thisissometext && printf "%s\n" "$i"; done' _ {} +
Команда bash
, развернутая:
for i
do
tr -d "[[:space:]]" < "$i" |
grep -q thisissometext &&
printf "%s\n" "$i"
done
Это цикл по всем аргументам и использует указанный выше тест.
решение3
Код ниже рекурсивно ищет файлы в каталоге, удаляет все вхождения " "
и "\n"
. Если строка существует в оставшемся тексте, есть совпадение. Это означает, что пробелы/переводы строк могут быть налюбойположение в строке внутри вашего файла(ов).
Что оно делает
Если будут найдены соответствующие файлы, они будут выведены в терминал, включая пути к ним, например:
/home/jacob/Bureaublad/testmap/test2.txt
/home/jacob/Bureaublad/testmap/Naamloze map 2/test1.txt
Я встроил try / except, чтобы предотвратить сбой скрипта, если он столкнется с нечитаемым файлом.
Сценарий
#!/usr/bin/env python3
import os
import sys
s = sys.argv[2]
for root, dirs, files in os.walk(sys.argv[1]):
for file in files:
file = root+"/"+file
try:
if s in open(file).read().replace(" ", "").replace("\n",""):
print(file)
except:
pass
Как использовать
- Скопируйте скрипт в пустой файл, сохраните его как
find_string.py
Запустите его, указав каталог и строку в качестве аргументов:
python3 /path/to/find_string.py <directory> <string_to_find>
Если строка или каталог содержат пробелы, используйте кавычки:
python3 /path/to/find_string.py '<directory>' '<string_to_find>'
Примечание
Скрипт, как он есть, находит файлы со строкой, содержащей либо пробелы, либо переводы строк. Он может быть расширен другими символами/строками (например, табуляциями) в строке:
if s in open(file).read().replace(" ", "").replace("\n",""):
решение4
Вы можете использовать grep -i --recursive 'word1\|word2' *
и awk '/word1/,/word2/'
можете использовать для работы с новой строкой