Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent Powershell from adding new line at the end of file

I needed to replace HTML entities in the files within several subfolders, so I used PowerShell script suggested here: https://stackoverflow.com/a/2837891

However, that script adds an extra new line to the end of file and I'd like to avoid that. There was another script listed in the very next comment in that thread (https://stackoverflow.com/a/2837887) which was supposed to achieve exactly what I need, but it didn't work when I tried running it.

Here is my script:

$configFiles = Get-ChildItem . *.xml -rec
foreach ($file in $configFiles)
{
    (Get-Content $file.PSPath) |
    Foreach-Object { $_ -replace '&# 8211;','–' } |
    Foreach-Object { $_ -replace '&# 160;',' ' } |
    Foreach-Object { $_ -replace '&# 8221;','”' } |
    Set-Content $file.PSPath
}

All I need to do is for it NOT to be adding a new line at the end.

Thank you in advance!

like image 872
BN_pdx Avatar asked Mar 09 '23 08:03

BN_pdx


1 Answers

PowerShell v5+ supports the -NoNewline switch with the Set-Content cmdlet (and also Add-Content and Out-File).

If you're running an earlier version, you must use the .NET Framework directly, as demonstrated in one of the answers you link to.

Caveat: -NoNewline doesn't just mean that a trailing newline is omitted, but that all input objects are concatenated directly, without a separator (and without adding a trailing newline).
If your input is a single multi-line string, as below, -NoNewLine will work as expected, but if you have an array of strings you want to write with newlines only between them and not also a trailing one, you'll have to do something like:
(('one', 'two', 'three') -join "`n") + "`n" | Set-Content -NoNewLine $filePath).
See also: this answer of mine.


As an aside: There's no need for multiple ForEach-Object calls or even a foreach statement; you can do it all in one pipeline (PSv3+, due to Get-Content -Raw, but you can omit -Raw to make it work (less efficiently) in PSv2 too):

Get-ChildItem . *.xml -Recurse |
  ForEach-Object { 
    $filePath = $_.FullName 
    (Get-Content -Raw $filePath) -replace '&# 8211;', '–' `
       -replace '&# 160;', ' ' `
          -replace '&# 8221;', '”' |
            Set-Content -NoNewline $filePath
  }

Optional reading:

TheMadTechnician points out that an alternative to defining variable $filePath to refer to the full path of the input file at hand inside the script block of the ForEach-Object call is to use common parameter -PipelineVariable (-pv):

Get-ChildItem . *.xml -Recurse -PipelineVariable ThisFile |
  ForEach-Object { 
    (Get-Content -Raw $ThisFile.FullName) -replace '&# 8211;', '–' `
       -replace '&# 160;', ' ' `
          -replace '&# 8221;', '”' |
            Set-Content -NoNewline $ThisFile.FullName
  }

Note how the argument passed to PipelinVariable must not have the $ prefix, because it is the name of the variable to bind.
$ThisFile then refers to Get-ChildItem's current output object in all subsequent pipeline segment(s).

While there's not much to be gained in this particular case, the general advantage of using
-PipelinVariable is that the variable bound this way can be referenced in any subsequent pipeline segment.

like image 199
mklement0 Avatar answered Mar 31 '23 19:03

mklement0