Сценарий Python для поиска в дереве

scenarij python dlya poiska v dereve Законченные системные программы

Наконец, после экспериментов с инструментами grep, glob и find для упрощения глобального поиска на всех платформах, которые могут мне когда-либо встретиться, я написал сценарий на языке Python, который выполняет основную работу вместо меня. В примере 6.17 применяются стандартные средства Python, с которыми мы познакомились в предыдущих главах: os.walk — для обхода файлов в каталоге, os.path. splitext — для пропуска файлов с расширениями, характерными для двоичных файлов, и os.path.join — для переносимого объединения путей к каталогам с именами файлов.

Поскольку он написан исключительно на языке Python, этот сценарий в равной степени может использоваться и в Linux, и в Windows. На самом деле он должен работать на любом компьютере, где установлен Python. Более того, благодаря непосредственному использованию системных вызовов он должен работать быстрее, чем при использовании приема запуска команды оболочки.

Пример 6.17. PP4E\Tools\search_all.py

############################################################################## Порядок использования: “python \Tools\search_all.py dir string”.

Отыскивает все файлы в указанном дереве каталогов, содержащие заданную строку; для предварительного отбора имен файлов использует интерфейс os.walk вместо find.find; вызывает visitfile для каждой строки в результатах, полученных вызовом функции find.find с шаблоном “*”;

##############################################################################

import os, sys

listonly = False

textexts = [‘.py’, ‘.pyw’, ‘.txt’, ‘.c’, ‘.h’] # игнорировать двоичные файлы

def searcher(startdir, searchkey):

global fcount, vcount

fcount = vcount = 0

for (thisDir, dirsHere, filesHere) in os.walk(startdir):

for fname in filesHere: # для каждого некаталога

fpath = os.path.join(thisDir, fname) # fname не содержит пути visitfile(fpath, searchkey)

def visitfile(fpath, searchkey): # для каждого некаталога

global fcount, vcount # искать строку

print(vcount+1, ‘=>’, fpath) # пропустить защищенные файлы

try:

if not listonly:

if os.path.splitext(fpath)[1] not in textexts: print(‘Skipping’, fpath)

elif searchkey in open(fpath).read():

input(‘%s has %s’ % (fpath, searchkey)) fcount += 1

except:

print(‘Failed:’, fpath, sys.exc_info()[0])

vcount += 1

if __name__ == ‘__main__’:

searcher(sys.argv[1], sys.argv[2])

print(‘Found in %d files, visited %d’ % (fcount, vcount))

Функционально этот сценарий делает примерно то, что мы получили бы, вызвав его функцию visitfile для всех строк, сгенерированных нашей функцией find.find с шаблоном «*». Но поскольку эта версия настроена на поиск по содержимому файлов, она лучше соответствует своей цели. В действительности это сходство обусловлено лишь использованием шаблона «*», который вынуждает find.find выполнить обход всех файлов, а это, собственно, то, чем занята новая функция searcher. Инструмент поиска хорошо подходит для выбора файлов определенного типа, при этом преимущество данного сценария состоит в возможности произвести определенные действия непосредственно в процессе обхода.

При запуске в виде самостоятельного сценария ключ поиска передается в командной строке, а при импортировании клиент вызывает функцию searcher непосредственно. Например, чтобы найти все вхождения строки в дереве примеров для книги, выполните в команду в оболочке DOS или Unix, как показано ниже:

C:\\PP4E> Tools\search_all.py . mimetypes

1  => .\LaunchBrowser.py

2  => .\Launcher.py

3  => .\Launch_PyDemos.pyw

4  => .\Launch_PyGadgets_bar.pyw

5  => .\__init__.py

6  => .\__init__.pyc

Skipping .\__init__.pyc

7  => .\Preview\attachgui.py

8  => .\Preview\bob.pkl

Skipping .\Preview\bob.pkl

…множество строк опущено: ожидает нажатия клавиши Enter после обнаружения каждого совпадения…

Found in 2 files, visited 184

Сценарий выводит список всех проверяемых им файлов, сообщает о пропущенных файлах (имена с расширениями, отсутствующими в переменной textexts, которые, как предполагается, являются двоичными файлами) и останавливается, ожидая нажатия клавиши Enter после вывода сообщения о нахождении в файле искомой строки. Точно так же сценарий search_all работает и при импортировании, но не выводит итоговой строки со статистикой (функции fcount и vcount находятся в модуле, и их также можно импортировать, чтобы получить итоговые сведения):

C:\\PP4E\dev\Examples\PP4E> python

>  >> import Tools.search_all

>  >> search_all.searcher(r’C:\temp\PP3E\Examples’, ‘mimetypes’)

> .. множество строк опущено: останавливается 8 раз в ожидании нажатия клавиши Enter

>  >> search_all.fcount, search_all.vcount # совпадений, файлов (8, 1429)

Каким бы образом ни запускался этот сценарий, он находит все вхождения искомой строки в целом дереве каталогов — например, изменившееся имя файла примера, объекта или каталога. Это именно то, что мне было необходимо, — по крайней мере, я так думал, пока не погрузился в дальнейшие размышления, пытаясь отыскать более полные и лучше структурированные решения, о которых рассказывается в следующем разделе.

Обязательно ознакомьтесь с обсуждением регулярных выражу жений в главе 19. В данном случае сценарий search_all пыта- *’» еется отыскать в каждом файле простую строку, применяя  ■ простое выражение проверки на вхождение, однако его легко можно было усовершенствовать, добавив возможность поиска по регулярному выражению (грубо говоря, для этого достаточно заменить оператор in вызовом метода поиска объекта регулярного выражения). Разумеется, это усовершенствование покажется вам намного проще, когда мы узнаем, как это делается.

Обратите также внимание на список textexts в примере 6.17. В нем перечислены все допустимые расширения текстовых файлов. Решение можно сделать более универсальным и надежным, если использовать логику модуля mimetypes, с которым мы познакомимся ближе к концу этой главы, который позволяет делать предположения о типе содержимого файла по его имени. Однако список допустимых расширений обеспечивает более точное управление, и его вполне достаточно для деревьев, поиск по которым я осуществлял с помощью этого сценария.

Наконец, обратите внимание, что для простоты во всех примерах поиска по дереву каталогов в этой главе предполагается, что текстовые файлы содержат текст Юникода, закодированный с применением кодировки по умолчанию, используемой платформой. Чтобы избежать ошибок при декодировании, в примерах можно было бы открывать текстовые файлы в двоичном режиме, но при этом результаты поиска могут оказаться неточными, если сравниваемые строки байтов будут закодированы с применением различных схем кодирования. Более удачное решение вы найдете в реализации утилиты «grep» в примере приложения PyEdit с графическим интерфейсом, где обеспечивается возможность применения указанной пользователем кодировки и пропускаются те текстовые или двоичные файлы, попытка декодирования которых завершается неудачей.

Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011

Каталог сайтов Всего.ру
Оцените статью
Секреты программирования
Добавить комментарий