Выполнение PowerShell-аналога *nix grep

Выполнение PowerShell-аналога *nix grep

Есть ли способ оптимизировать код PowerShell ниже (он извлекает отдельные строки из нескольких текстовых файлов в один):

$ErrorActionPreference = "Continue"
Start-Transcript -path D:\0xAC1CC07A.log -append
$OutFile = "D:\0xAC1CC07A.txt"
echo "filtering 0xAC1CC07A"
ForEach ($filenm in ((get-childitem -Path D:\FILES\* -include ubuntlive1mb_?????_201509*.txt -recurse -force))) 
{
 $filenm.fullName;
 (Get-Content $filenm) | select-string "0xAC1CC07A" | Add-Content $OutFile
}
Stop-Transcript

Он хорошо справляется с небольшими рабочими нагрузками, но на 160K текстовых файлов (более 200 ГБ в общей сложности) он работает более 4 дней на моей виртуальной машине Win2008R2. Удивительно, но Ubuntu 14.04 на аналогичном виртуальном оборудовании справился с этой задачей за 4 часа:

grep --no-filename "0xac1cc07a" ./FILES/ubuntlive1mb_?????_201509*.txt >>./0xAC1CC07A.txt

Или точнее:

find ./FILES -name "ubuntlive1mb_?????_201509*.txt" -type f -print0 | xargs -0 grep --no-filename "0xac1cc07a" $1 >>./0xAC1CC07A.txt

Я не силен ни в PowerShell, ни в *nix, все вышеприведенные скрипты были созданы путем поиска в Google и копирования-вставки.

Файловая система Windows была оптимизирована путем отключения имен файлов DOS и обновления каталогов в списке. Ubuntu был просто установлен из коробки.

решение1

Этот очень простой скрипт Powershell должен сделать то, что вам нужно:

$OutFile = "D:\0xAC1CC07A.txt"
Get-ChildItem -Path D:\FILES\ubuntlive1mb_?????_201509*.txt -Recurse | Foreach-Object { Select-String -Path $_ -Pattern "0xAC1CC07A" } | Foreach-Object { Add-Content -Path $OutFile -Value $_.Line }

Это просто добавит совпавшие строки в текстовый файл $OutFile. Вы также можете использовать это, чтобы получить имена файлов или номера строк совпавших строк, используя свойства Filename, Path и LineNumber, а не только свойство Line.

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

Пример:

Get-ChildItem -Path D:\FILES\ubuntlive1mb_?????_201509*.txt | Select-Object -First 100 | Foreach-Object { Select-String -Path $_ -Pattern "0xAC1CC07A" } | Foreach-Object { Add-Content -Path $OutFile -Value $_.Line }

Это запустит указанный выше скрипт только для первых 100 текстовых файлов, возвращенных Get-ChildItem.

решение2

Вы получите немного другой вывод (но это можно исправить, если возникнет такая необходимость), но, насколько я видел, гораздо быстрее просто выполнить Select-String непосредственно в файле, а не сначала получать содержимое файла.

Select-String "0xAC1CC07A" -Path $filenm.FullName | Add-Content $OutFile

Просто не забудьте сначала проверить вывод, прежде чем добавлять его в файл, чтобы получить желаемый результат.

Что касается скорости, то Get-ChildItem печально известен своей медленной работой в PowerShell (поскольку PowerShell предпочитает извлекать объекты, а не просто текстовое представление объектов), и для этого существуют различные обходные пути.

Однако Get-ChildItem-line в вашем коде можно оптимизировать. Из того, что я видел, использование Filter примерно в 3,5 раза быстрее, чем использование include/excludes на обычном потребительском HDD 7.2k.

Get-ChildItem -Path "D:\FILES" -Filter "ubuntlive1mb_?????_2015090101*.txt" -Recurse -Force

Если мне не изменяет память, в более ранних версиях PowerShell были некоторые проблемы с фильтрацией, например, если вы хотели отфильтровать все файлы с расширением .htm, он также выбирал файлы с расширением .html (как будто вы отфильтровали, *.htm*а не *.htm), так что вам стоит обратить на это внимание.

Связанный контент