30

I want to load a file template into a variable, modify data within the variable and output the modified template to a new location from the variable.

The issue is that PowerShell is removing newlines from my template.

The input file (template file) has Unix line endings which are also required for output since the recipient of the modified version is a Unix-based system.

I have the following code which results into a concatted one-liner:

[String] $replacement = "Foo Bar"
[String] $template = Get-Content -Path "$pwd\template.sh" -Encoding UTF8
$template = $template -replace '<REPLACE_ME>', $replacement
$template | Set-Content -Path "$pwd\script.sh" -Encoding UTF8

Having the template input:

#!/bin/sh
myvar="<REPLACE_ME>"
echo "my variable: $myvar"
exit 0

Resulted into:

#!/bin/sh myvar="Foo Bar" echo "my variable: $myvar" exit 0

It appears to me that somewhere LF where replaced by one simple whitespace. Finally at the end of the script there is an added CR LF which was not present in the template file.

How do I preserve the line endings and prevent adding further (CR LF) wrong line endings to the final script?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
burnersk
  • 3,320
  • 4
  • 33
  • 56

4 Answers4

37

For the $replacement variable, you don't really need to specify the type [string], PowerShell will infer that from the assignment.

For the $template variable, [string] is actually wrong. By default, Get-Content will give you an array of strings (i.e. lines) instead of one string.

But in fact you don't even want to split the input into lines in the first place. When Set-Content or Out-File see an array as their input, they will join it with spaces.

Using -Raw makes Get-Content return the entire file as one string, this way also the line endings (like LF for Linux files) will stay the way they are.

$replacement = "Foo Bar"
$template = Get-Content -Path "$pwd\template.sh" -Encoding UTF8 -Raw
$template = $template -replace '<REPLACE_ME>', $replacement
Set-Content -Path "$pwd\script.sh" -Value $template -Encoding UTF8

PowerShell will save all UTF-8 files with a BOM. If you don't want that, you must use a different utility to write the file:

$UTF8_NO_BOM = New-Object System.Text.UTF8Encoding $False

$replacement = "Foo Bar"
$template = Get-Content -Path "$pwd\template.sh" -Encoding UTF8 -Raw
$template = $template -replace '<REPLACE_ME>', $replacement
[System.IO.File]::WriteAllText("$pwd\script.sh", $template, $UTF8_NO_BOM)

Notes:

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • The "missing newline situation" is solved with this solution. **However** I now have all `CR LF` instead of just 'LF' which makes UNIX systems fail when the script is being executed. The newlines within the template are definitely just `LF`. Could you expand your answer, please? – burnersk Nov 27 '17 at 14:34
  • 1
    Huh? If you use `-Raw` you should not be in this situation. – Tomalak Nov 27 '17 at 14:35
  • 1
    Yeah, I've modified the answer quite a bit because my own assumptions about the origin of the spaces were wrong. – Tomalak Nov 27 '17 at 14:45
6

Use the -delimiter "`n" option instead of -raw. The -raw option reads/returns the entire content as a single string, although it preserves the new-line characters but it is useless if you need to manipulate the content e.g. skip Header/1st row or skip blank lines etc.

Get-Content - background info:

By default, the Get-Content cmdlet reads & returns content line-by-line, which means if you pipe a Set-Content or Add-Content to instantly write each-line (being read) into the output file - the newline characters are preserved and written as expected, e.g.:

Get-Content $inputFile | Set-Content $outputFilePath 

However, if you store the entire content (read) into a variable (called $variable), your variable will only receive a single string-array WITHOUT the separator/delimiter (by default), which means you will lose the new-line characters, however, when reading file (using Get-Content) you can use the -delimiter option to specify a newline character (`n), which will then be preserved and written/stored into your $variable, e.g.:

Get-Content -Delimiter "`n" $fileToRead

HTH.

Eddie Kumar
  • 1,216
  • 18
  • 20
3

I think you need to use the -Raw switch with Get-Content in order to load the file as a single string:

[String] $replacement = "Foo Bar"
[String] $template = Get-Content -Path "$pwd\template.sh" -Encoding UTF8 -Raw
$template = $template -replace '<REPLACE_ME>', $replacement

To stop the Windows line ending being added to the end of the script, I think you need to use this .NET method for writing the file:

[io.file]::WriteAllText("$pwd\template.sh",$template)

By default PowerShell attempts to convert your input in to an array of strings for each line in the file. I think because of the Unix line endings its not doing this successfully but is subsequently removing the new line characters.

In PowerShell 3.0 we now have a new dynamic parameter, Raw. When specified, Get-Content ignores newline characters and returns the entire contents of a file in one string. Raw is a dynamic parameter, it is available only in file system drives.

Mark Wragg
  • 22,105
  • 7
  • 39
  • 68
0

I was using Get-Content-Tail, which doesn't allow you to specify -Raw at the same time, but I did have luck with Out-String. So, in your case:

$template = Out-String -InputObject $( Get-Content -Path "$pwd\template.sh" -Encoding UTF8 -Raw)

Or perhaps, if you care about tail:

$template = Out-String -InputObject $(Get-Content -Path "$pwd\template.sh" -tail 4)
Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77