Home > database >  Spliting value of new-item and store in text file
Spliting value of new-item and store in text file

Time:07-31

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, whereas Set-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
#>
  • Related