В выходных файлах отсутствует перевод строки между строками данных - PullRequest
0 голосов
/ 12 марта 2019

Я передаю в массиве $users.

PS C:\> $users | ft

ID     DisplayName           AdminID   first  last   Password
----   -----------           -------   -----  ----   --------
Axyz   Axyz, Bill            NBX_Admin Bill   Axyz   Secret

Код:

$y = @()
$y = "Create Users process.  Run started at $('[{0:MM/dd/yyyy} {0:HH:mm:ss}]' -f (Get-Date))"
foreach ($x in $users) {
    $y += "User $($x.DisplayName) with NNN of $($x.ID)"
}
$y += "Completed at $('[{0:MM/dd/yyyy} {0:HH:mm:ss}]' -f (Get-Date))"
$y | Out-File "Log.txt"

$y теперь неформатированный строковый массив. Когда я набираю $y на экран, он выглядит великолепно.

Если я направлю его на Format-Table, он выглядит великолепно (без заголовков).

Когда я выводю его в файл и набираю его в командной строке (cmd.exe), он выглядит великолепно.

Однако, когда я поднимаю его в блокноте, все выходные данные отображаются в одной строке. Если быть точным, все данные есть, нет пропущенных строк данных, но нет CR / LF, поэтому все данные отображаются в одной строке в файле при просмотре с помощью Notepad.exe.

Ответы [ 2 ]

1 голос
/ 13 марта 2019

Как AdminOfThings правильно указывает:

  • Хотя $y = @() назначает пустой массив для $y, он не ограничение типа этой переменной, поэтому ваше следующее присвоение - $y = "Create Users process ..." - изменяет тип переменной на строку .

    • Простое использование += вместо = в этом последующем присваивании предотвратило бы проблему: $y += "Create Users process ...".
    • В качестве альтернативы ограничение типа переменнаясоздание - [array] $y = @() - то есть размещение литерала типа слева от присваиваемой переменной (сродни приведению) - также предотвратило бы проблему.
  • Последующее использование +=, следовательно, выполняет простую конкатенацию строк вместо желаемого постепенного построения массива без добавления разделителей между «строками». [1]

    • В отличие от , если бы вы использовали массив , как предполагалось, как Out-File, так и Set-Content будет автоматически вставлять соответствующие элементы платформы [2] между элементами, плюс один в конце, при сохранении (в PSv5 + вы можете использовать переключатель -NoNewlineотказаться).

Тем не менее, использование += для "расширения" массива неэффективно, потому что PowerShell должен сделать за сценой создание новый массив , содержащий старые элементы плюс новые элементы, учитывая, что массивы имеют структуру фиксированного размера .

В то время как снижение производительности при использовании += для«Расширение» массивов в цикле действительно имеет значение только при большом количестве итераций, это более кратко, удобно и эффективно, чтобы позволял PowerShell создавать массивы для вас неявно , используя ваш foreach цикл в качестве выражение :

# Initialize the array and assign the first element.
# Due to the type constraint ([array]), the RHS string implicitly becomes
# the array's 1st element.
[array] $y = "Create Users process.  Run started at $('[{0:MM/dd/yyyy} {0:HH:mm:ss}]' -f (Get-Date))"

# Add the strings output by the foreach loop to the array.
# PowerShell implicitly collects foreach output in an array when
# you use it in as an expression.
$y += foreach ($x in $users)
{
    "User $($x.displayname) with NNN of $($x.ID)"
}   

# Add the final string to the array.
$y += "Completed at $('[{0:MM/dd/yyyy} {0:HH:mm:ss}]' -f (Get-Date))"

# Send the array to a file with Out-File, which separates 
# the elements with newlines and adds a trailing one.
# Windows PowerShell:
#   Out-File creates UTF-16LE-encoded files.
#   Set-Content, which can alternatively be used, creates "ANSI"-encoded files.
# PowerShell Core:
#   Both cmdlets create UTF-8-encoded files without BOM.
$y | Out-File "Log.txt"

Обратите внимание, что вы можете аналогичным образом использовать операторы for, if, do / while / switch каквыражения .

Во всех случаях, однако, на PowerShell 6.2.0, тэти выражения могут служить только выражениями сами по себе ;к сожалению, встраивание их в большие выражения не работает - см. этот выпуск GitHub .


[1] Простая демонстрацияВаша проблема:

# The initialization of $y as @() is overridden by $y = 'first'.
PS> $y = @(); $y = 'first'; $y += 'second'; $y
firstsecond  # !! $y contains a single string built with string concatenation

Поэтому описание ваших симптомов не соответствует вашему коду, поскольку вы должны были видеть однострочную строку вывода во всех сценариях (печать прямо на экран / через Format-Table, отправка в файл и type -ing, что из cmd.exe).

[2] Соответствующий платформе символ новой строки отражается в [Environment]::NewLine, и это "`r`n" (CRLF) в Windowsи просто "`n" (LF) на Unix-подобных платформах (в PowerShell Core ).

1 голос
/ 12 марта 2019

Поскольку при использовании += воссоздается массив на каждой итерации, я бы предложил назначить выходные данные ForEach-Object с его -Begin, -Process и -End переменной, также используя более общий подходоператора формата .:

$Log = $users | ForEach-Object -Begin {
    "Create Users process.  Run started at [{0:MM/dd/yyyy} {0:HH:mm:ss}]" -f (Get-Date)
} -Process {
    "User {0} with NNN of {1}" -f $_.DisplayName,$_.ID
} -End {
    "Completed at [{0:MM/dd/yyyy} {0:HH:mm:ss}]" -f (Get-Date)
}
$Log | Set-Content "Log.txt"
...