Есть ли способ оптимизировать код 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
), так что вам стоит обратить на это внимание.