beginner in powershell and trying to get better.
I am creating a text file in powershell with 3 lines
new-item -itemtype file -Value "1,2,3" modules.txt
However the output of it is "1,2,3" instead of a new line for each item
1
2
3
I tried new-item -itemtype file -Value "1,2,3" -split "," modules.txt
But could not use that parameter inline since it would not be recognized. Also tried saving it to an array.
$array = '1,2,3' -split ','
However when running
new-item -itemtype file -Value "$array" modules.txt
It would all still appear in one line
Grateful for any advice
CodePudding user response:
New-Item
doesn't meaningfully support an array of values to write to the new file, neither via an argument passed to its -Value
parameter nor via the pipeline.
You have two options:
Either: Create a multi-line representation of your array yourself, with each (stringified) element on its own line, using the
-join
operator:# Note: [Environment]::NewLine adds a *trailing* newline, # which may or may not be necessary. New-Item modules.txt -Value ( 1, 2, 3 -join [Environment]::NewLine [Environment]::NewLine )
As zett42 points out, a simpler alternative is to use
Out-String
, which automatically appends a trailing newline to its output string,[1] and stringifies complex objects using PowerShell's rich output formatting system, not by calling the.ToString()
method on each element the way-join
implicitly does (for strings and primitive types such as numbers, this won't make a difference):New-Item modules.txt -Value (1, 2, 3 | Out-String) # Ditto, via the pipeline. This is possible, because # Out-String creates a *single* object, namely a multi-line string. 1, 2, 3 | Out-String | New-Item modules.txt
Or: Use
Set-Content
instead, which implicitly creates the file (see caveat below); its-Value
parameter / pipeline input processing does handle arrays properly:Set-Content modules.txt -Value 1, 2, 3 # Ditto, via the pipeline (slower) 1, 2, 3 | Set-Content modules.txt
Caveats:
As a safety measure,
New-Item
refuses to create the target file if a file by that name already exists - you can use-Force
to override that.By contrast,
Set-Content
quietly overwrites a preexisting file, so you'd have to test for the existence of the target file first to prevent accidental overwriting.Character encoding:
In Windows PowerShell, where different cmdlets unfortunately use different default encodings (see the bottom section of this answer),
New-Item
creates BOM-less UTF-8 files by default, whereasSet-Content
defaults to ANSI encoding.Fortunately, in PowerShell (Core) 7 , BOM-less UTF-8 is now used consistently as the default, across all cmdlets.
As for what you tried:
... -Value "1,2,3" ...
Passing string literal "1,2,3"
predictably writes that string as-is to the target file (as verbatim value 1,2,3
).
... -Value "$array" ...
By enclosing (a variable containing) an array in "..."
, you implicitly stringify it, resulting in a single string that joins the (stringified) array elements with a space as the separator,[2] resulting in verbatim 1 2 3
. You can verify this with "$(1, 2, 3)"
.
[1] While helpful in this particular use case, this behavior is problematic in general - see GitHub issue #14444.
[2] Space is the default separator character. Technically, you can override it via the $OFS
preference variable, though that is rarely used in practice.
CodePudding user response:
Putting this here, since it is too long for a normal comment.
Ditto to 'mklement0' helpful answer.
Yet, there are always different ways to deal with one thing or the other, and the choice is yours.
If you want an array, why not just start with one? @(1,2,3) Right now you are using a single string and splitting it.
Again, 'mklement0', is the most direct, but, here is what I mean, because you say, you are creating the file, not that you are reading a file you are given, thus you have complete control of this creation effort:
(again, just giving you food for thought):
# Create a new array, assigning the results to a variable and output to screen
($array = @(1,2,3))
# Or as a string set
($array = @('1','2','3'))
# Results using either of the above, no extra splittng required
<#
1
2
3
#>
$array.Count
# Results
<#
3
#>
# Create a text file of the results
# Remove existing file, if it exists
Remove-Item -Path 'modules.txt' -ErrorAction SilentlyContinue
$array |
ForEach-Object {Add-Content -Path 'modules.txt' -Value $PSitem}
Get-Content -Path 'modules.txt'
# Results
<#
1
2
3
#>
# Or using what you have
# Remove existing file, if it exists
Remove-Item -Path 'modules.txt' -ErrorAction SilentlyContinue
'1,2,3' -split ',' |
ForEach-Object {Add-Content -Path 'modules.txt' -Value $PSitem}
Get-Content -Path 'modules.txt'
# Results
<#
1
2
3
#>
# Here is another way, using what you show, using string -replace vs string split
# Remove existing file, if it exists
Remove-Item -Path 'modules.txt' -ErrorAction SilentlyContinue
($array = ('1,2,3')) -replace ',',"`r`n" |
Out-File -FilePath 'modules.txt'
Get-Content -Path 'modules.txt'
# Results
<#
1
2
3
#>
# Remove existing file, if it exists
Remove-Item -Path 'modules.txt' -ErrorAction SilentlyContinue
$array = '1,2,3' -replace ',',"`r`n" |
Out-File -FilePath 'modules.txt'
Get-Content -Path 'modules.txt'
# Results
<#
1
2
3
#>