Powershell gotchas: standard out breaks lines to fit console

When Powershell writes to standard out, and there is now redirection in the Powershell script itself, it will assume that it writes to a console. Because of this, it kindly breaks lines if they are longer than 80 characters. So, given the following powershell file (.ps1):

# line-gunk.ps1
"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"

If you run this Powershell file from the Windows command line, and redirects output to a file:

C:>@powershell -file .line-gunk.ps1 > foo.txt

Then you will get:

1234567890123456789012345678901234567890123456789012345678901234567890123456789
01234567890

However, if you do the redirection in Powershell, it’s fine:

PS C:> .line-gunk.ps1 > bar.txt

A näive fix to this, would be to change your script to use Write-Host to explicity write the text to the console. However, I consider this a bad practice because it would restrict the usage of your script as you could not manipulate the output further if reusing the script. I found this entry on stackoverflow which provides a better solution. Going back to the original script, here is a fixed one that worked for me:

# line-gunk-fixed.ps1
$Host.UI.RawUI.BufferSize = New-Object Management.Automation.Host.Size(500, 25)
"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"

Notice that I used the simplifed version from the stackoverflow entry, which might not work in all circumstances.

Backstory

How did I come across this problem? I running Powershell build scripts in TeamCity and I was trying to report the build progress back to TeamCity. However, it somehow did not work. I then discovered that the status messages written by my script were broken by the line breaks, rendering them useless…

Running chocolatey behind an authenticating firewall

I long grappled with a problem installing applications using chocolatey. Specifically, all installations that required downloading an MSI (or similar) file outside the .nupkg caused the following error message:

The remote server returned an error: (407) Proxy Authentication Required.

Yes, I am behind a firewall that requires authentication. I found several references to this error message on the chocolatey forums, but they all seemed to be fixed in the latest chocolatey version.

Through some googling I was able to track down the problem. Acutally, it is a problem with the .NET 3.5 runtime that causes the System.Net.WebClient to give up on an NTLM authentication challenge from proxies. Chocolatey uses Powershell, and indeed the System.Net.WebClient to download installation packages (all though not the package manifest itself, where it uses NuGet). As you my or may not know, Powershell uses the .NET 3.5 runtime per default.

Hence, the fix was to make Powershell use the .NET 4.0 runtime, where this bug is fixed. I figured out how to do this based on this entry on stackoverflow.

I then wrote this little Powershell snippet which changes Powershell on my machine to use .NET 4.0 instead of .NET 3.5:

if ([Environment]::Version.Major -lt 4) {
	$configFile = Join-Path $PSHOME -ChildPath '.powershell.exe.config'
	if (-not(Test-Path $configFile)) {
		@'
<?xml version="1.0"?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0.30319" />
    <supportedRuntime version="v2.0.50727" />
  </startup>
</configuration>
'@ | Out-File -FilePath $configFile -Encoding UTF8
		"Created $configFile"
	}
	'Restart powershell in order to make it run in .NET 4'
}

Powershell gotchas: getting multiline string literals correct

The gotcha here is that the opening token @' or @" has to be at the end of the line, and the closing token, '@ or "@, has to be a the beginning of a line.

Wrong:

@'1
2
3
'@
Unrecognized token in source text.
At ...ps1:1 char:1
+  <<<< @'1
    + CategoryInfo          : ParserError: (:) [], ParseException
    + FullyQualifiedErrorId : UnrecognizedToken
@'
1
2
3'@
The string starting:
At ...:12 char:6
+ $s =  <<<< @'
is missing the terminator: '@.
At ...ps1:34 char:3
+ #> <<<<
    + CategoryInfo          : ParserError: (1
2
3'@
:String) [], ParseException
    + FullyQualifiedErrorId : TerminatorExpectedAtEndOfString

Correct:

@'
1
2
3
'@
1
2
3