Powershell: меньший файл занимает в 30 раз больше времени для записи с помощью Out-File - PullRequest
1 голос
/ 20 марта 2020

Я получаю содержимое файла и сохраняю только те строки, которые соответствуют регулярному выражению или пустой строке. Но написание результатов, например, меньшее количество данных, занимает много времени ... Вот код, о котором идет речь (я добавил несколько строк для отладки / измерения):

$original = Get-Content "$localDir\$ldif_file"
(Measure-Command -Expression { $original | Out-File "$localDir\Original-$ldif_file" }).TotalSeconds
$lines = ($original | Measure-Object -Line).Lines
"lines of `$original = $lines"

# Just keep lines of interest:
$stripped = $original | select-string -pattern '^custom[A-Z]','^$' -CaseSensitive
$lines = ($stripped | Measure-Object -Line).Lines
"lines of `$stripped = $lines"
(Measure-Command -Expression { $stripped | Out-File "$localDir\Stripped-$ldif_file" }).TotalSeconds

"done"

Проблема: требуется для записи в файл меньших данных ($ stripped) 342 секунды (примерно в 30 раз больше, чем исходные данные $)! Смотрите вывод ниже:

11.5371677
lines of $original = 188715
lines of $stripped = 126404
342.6769547
done

Почему исходный файл $ извлекается намного медленнее, чем исходный $? Как это улучшить?

Спасибо!

Ответы [ 2 ]

3 голосов
/ 20 марта 2020

В дополнение к полезный ответ Матиаса :

  • В PowerShell 7 , Select-String теперь поддерживает -Raw switch , который выводит только строки (соответствующие строки), что должно значительно ускорить выполнение команды.

    • В Windows PowerShell , менее эффективно, вы можете заключить вызов Select-String в (...).Line, чтобы получить строки только в виде строк.

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

  • Как правило, для записи объектов, которые уже являются строками , Set-Content лучший - и более быстрый - выбор по сравнению с Out-File.

    • См. этот ответ для справочной информации и нижнюю часть этот ответ для выполнения Сравнение.

    • Предостережение о кодировке символов (см. этот ответ для фона):

      • In Windows PowerShell , Set-Content по умолчанию соответствует кодировке ANSI, тогда как Out-File по умолчанию равно "Unicode" (UTF-16LE); при необходимости используйте -Encoding.
      • К счастью, PowerShell [Core] 6 + использует согласованное значение по умолчанию, а именно UTF-8 без спецификации.
  • Передача коллекций по конвейеру может быть медленной; для коллекций уже в памяти в полном объеме заметно быстрее передать их как в целом , в качестве аргумента вместо - при условии целевой командлет поддерживает - Set-Content -Value параметр делает .

Чтобы собрать все вместе:

# *PowerShell 7*: Use -Raw to directly get the lines as string.
$stripped = $original | 
  Select-String -Raw -Pattern '^custom[A-Z]','^$' -CaseSensitive

# *Windows PowerShell*: Use (...).Line to get the lines as strings.
$stripped = ($original | 
  Select-String -Pattern '^custom[A-Z]','^$' -CaseSensitive).Line

$lines = $stripped.Count # Simply count the array elements == number of lines.
"lines of `$stripped = $lines"

(Measure-Command -Expression { 
  Set-Content "$localDir\Stripped-$ldif_file" -Value $stripped
}).TotalSeconds
1 голос
/ 20 марта 2020

Вы действительно сравниваете яблоки и апельсины здесь.

$original содержит 189K строк , но $ stripped содержит 126K MatchInfo объектов, которые должны быть преобразованы в строки один за другим в конвейере.

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

$original = Get-Content "$localDir\$ldif_file"
(Measure-Command -Expression { $original | Out-File "$localDir\Original-$ldif_file" }).TotalSeconds
$lines = ($original | Measure-Object -Line).Lines
"lines of `$original = $lines"

# Just keep lines of interest:
$stripped = $original |Where-Object {$_ -cmatch '^custom[A-Z]' -or $_ -like ''}
$lines = ($stripped | Measure-Object -Line).Lines
"lines of `$stripped = $lines"
(Measure-Command -Expression { $stripped | Out-File "$localDir\Stripped-$ldif_file" }).TotalSeconds

"done"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...