3

Есть ли способ оптимизации кода 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

Он хорошо справляется с небольшими рабочими нагрузками, но на 160 КБ текстовых файлов (всего более 200 ГБ) работает на моей виртуальной машине Win2008R2 более 4 дней. Удивительно, но 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 box оптимизирована для файловой системы, отключив имена файлов dos и обновление каталога в списке. Ubuntu была только что установлена из коробки.

2 ответа2

2

Этот очень простой скрипт 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 в вашем коде может быть оптимизирована. Из того, что я видел, использование фильтра примерно в 3,5 раза быстрее, чем использование включений / исключений на обычном жестком диске 7,2 К потребительского уровня.

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

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .