Я новичок в экосистеме Windows. Мне поручили написать программу, которая будет искать определенную строку в нескольких десятках (может быть, даже в сотнях) тысяч файлов. Строка, которая должна быть сопоставлена, представляет собой серийный номер, состоящий только из цифр и букв, и имеет длину менее 20 символов. Прямо сейчас моя программа выполняет следующую команду:
findstr /i /m /s "searchStr" "C:\Directory\To\Search\*.*"
Вышеуказанная команда работает, однако она слишком медленная. Файл(ы), которые могут содержать определенный серийный номер, будут иметь серийный номер только в первой строке.
Кто-нибудь знает эффективный способ рекурсивного поиска в каталоге всех файлов, содержащих определенную строку только в первой строке?
решение1
В PowerShell (v3.0+) возможно...
Get-ChildItem -Path x:\pathto\*.log `
| ForEach-Object {
if (Get-Content -LiteralPath $_ -First 1 `
| Select-String -SimpleMatch -Pattern 'serialnumber')
{
Write-Output $_
}
}
Различные параметры Get-ChildItem
позволяют рекурсивно просматривать подпапки и т. д., получать Get-Content
больше или меньше содержимого из файла, а также Select-String
выполнять более сложные сопоставления (регулярные выражения, с учетом регистра и т. д.).
решение2
Я могу предложить несколько вариантов, если вам не нужно использовать findstr
, но в первую очередь вам следует посмотреть, можно ли ограничить поиск файлами определенного типа, так как это наверняка ускорит работу.
FileLocator Liteпо моему опыту, быстрее находит файлы и проверяет их содержимое. Обязательно заполните поля "имя файла" (если применимо) и "содержащийся текст", а также начальный каталог.
ag -il "searchStr"
:агсоздан для скорости, поэтому он должен быстро выдавать результаты. Обязательно ограничьте поиск по типу файла, если можете, хотя двоичные файлы по умолчанию уже пропускаются. Также доступно в разделеCygwin.find -exec awk 'BEGIN {IGNORECASE=1} NR==1 && /searchStr/ {print FILENAME": "$0}' {} \;
Попробуйте это, если у вас есть Cygwin или другая среда, подобная POSIX, чтобы проверить вашу идею поиска только в первой строке. Объедините,find
чтобы получить имена файлов (и, надеюсь, также отфильтровать их), иawk
проверьте первую строку и выведите ее вместе с именем файла.find | parallel 'perl -lane '\'' print "$ARGV: $_" if $. == 1 and /searchStr/i '\'' {}'
Еще одна идея, которая поможет ускорить процесс, — это заставить работать доступные ядра и потоки: вот чтоGNU-параллельэто for. Этот пример используетperl
, но он делает то же самое, чтоawk
и3.
выше. Вот разбор команды:find
искать файлы в текущем каталоге и его подкаталогах. Вы можете указать другой каталог для поиска и шаблон файла или расширение для фильтрации:find /cygdrive/c/Directory/To/Search -iname "*.txt"
.|
«pipe», т.е. передать список результатов следующей команде.parallel
параллельно выполнить следующую команду.perl
язык сценариев, который отлично подходит для работы с текстовыми файлами, может заменитьsed
илиawk
.-lane
полезный набор ключей для однострочных программ на Perl.'\''
экранированный апостроф, необходимый, так как мы уже открыли набор апострофа послеparallel
.print "$ARGV: $_"
выведите имя файла ($ARGV
), двоеточие, пробел и всю строку ($_
).if
выполнять предыдущую инструкцию только при соблюдении следующих условий.$. == 1
номер строки ($.
) равен единице (1
), т.е. мы смотрим на первую строку файла.and
также должно быть выполнено следующее условие./searchStr/i
проверяемая строка содержит текстsearchStr
без учета регистра.'\''
Еще один экранированный апостроф отмечает конецperl
инструкции.{}
это будет заменено наparallel
каждое из имен файлов, переданныхfind
.'
конецparallel
инструкции.
Обновлять:Оба awk
и perl
читают весь файл, даже если действия привязаны только к первой строке. Решение состоит в том, чтобы явно остановить разработку на строке 2:
find -exec awk 'BEGIN {IGNORECASE=1} NR > 1 {exit} /searchStr/ {print FILENAME": "$0}' {} \;
find | parallel 'perl -lape '\'' exit if $. == 2; print "$ARGV: $_" if /searchStr/i '\'' {}'