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!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With