Tag Archives: powershell gotcha

Powershell gotchas: refer to variables outside a function

Consider the following code:

$x = 1
function changeit {
    "Changing `$x. Was: $x"
    $x = 2
    "New value: $x"
}
"`$x has value $x"
changeit
"`$x has value $x"

Not too complicated, right. Let’s see what happens when we run it:

$x has value 1
Changing $x. Was: 1
New value: 2
$x has value 1

What’s going on? Well, trying to change the value of $x inside the function did not work. Why not? What the statement $x = 2 actually does, is to create a local variable inside the function and give it the value of 2. Let’s fix that:

$x = 1
function changeit {
    "Changing `$x. Was: $x"
    $script:x = 2
    "New value: $x"
}
"`$x has value $x"
changeit
"`$x has value $x"

Now, we run it again:

$x has value 1
Changing $x. Was: 1
New value: 2
$x has value 2

The thing is that we have to explicitly tell Powershell to update the variable in the parent scope instead of creating a new variable in the current scope.

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…

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

Powershell gotchas: redirect to file encodes in Unicode

The other day, I wrote a Powershell script that would manipulate a Windows command prompt file. After some troubleshooting (note to self: RTFM) I found Powershell file redirection encodes in Unicode, more specifically UCS-2 Little Endian. The problem in this case is that cmd.exe does not understand Unicode. So, the follwing yields a not runnable file:

@'
@echo off
echo Wi nøt trei a høliday in Sweden this ÿer?
'@ > '.Opening titles (unicode).cmd'
& '.Opening titles (unicode).cmd'
Output: '■@' is not recognized as an internal or external command,
operable program or batch file.

The trick is to use the Out-File Cmdlet instead and explicitly set the encoding. So, the following yields runnable code:

@'
@echo off
echo Wi nøt trei a høliday in Sweden this ÿer?
'@ | Out-file '.Opening titles (Ascii).cmd' -Encoding ASCII
& '.Opening titles (Ascii).cmd'
Output: Wi n?t trei a h?liday in Sweden this ?er?

In this case, it executes. However, the non-ASCII characters are not properly displayed. To achieve that as well, I used the following:

@'
@echo off
echo Wi nøt trei a høliday in Sweden this ÿer?
'@ | Out-file '.Opening titles (OEM).cmd' -Encoding OEM
& '.Opening titles (OEM).cmd'
Output: Wi nøt trei a høliday in Sweden this ÿer?