Working with PowerShell Core on Linux

Here are some useful notes I have been updating whenever I have some new findings.

TOC

Installation

First thing first, here is the official documentation on installing pwsh or pwsh-preview on Linux OS.

A known issue

This is a known issue that pwsh will hang after executing any external command if $TERM is screen, i.e. you are running pwsh inside a screen. However, according to the GitHub comment, it seems screen is to blame.

Though this does not limit its usage with other system utilities, I’d like to know if readers like you have some handy solution… Comment below!

Prolog

To start writing a PowerShell script, you may start with the following

#!/usr/bin/env pwsh

<#
.Synopsis
My PowerShell script!

.Description
This is the detailed description of the script.
#>

param()

# Code here.

Note here we use pwsh as the shell name. It does not match the preview version of PowerShell (pwsh-preview).

Exit code (from callees)

To retrieve the last exit code of an external executable, use $LastExitCode. This almost has the same meaning as $? in bash.

$? in PowerShell is a boolean value indicating whether the last command (Cmdlet or external executable) is successful or not.

Exit upon any error

Just like MS-DOS batch or Windows CMD batch script, PowerShell does not exit by default upon receiving non-terminating error records from the cmdlets or scriptlets. By error records I mean the records written with Write-Error cmdlet. This is what will happen if you try to cd into a non-existent folder, or to delete a file that you don’t have access to. Script will stop executing regardless when it receives Exception raised with throw expression.

Setting $ErrorActionPreference to "Stop" can stop the execution on the first error record.

Still, this does not affect failed external executables. So you might need an utility function to achieve this

# defintion
function checkLastExitCode() {
    if ($LASTEXITCODE) {
        throw [System.Exception]"Command exit code indicates failure: $LASTEXITCODE."
    }
}

# usage
git status
checkLastExitCode

Signals

Just like sh or bash, pwsh can work with other system utilities, such as systemd.service unit and cron. Like other .NET Core console applications, pwsh interprets SIGINT as Ctrl+C. If you want to capture such signal, you may consider using the approach mentioned in this SO post.

Another way to handle Ctrl+C in a more easier fashion, is to simply put your cleanup code inside finally part of a try…finally block. However, note that this block will be called no matter if your are exiting the try…finally block normally, with exception raised, or received Ctrl+C.

There is currently no built-in cmdlet for sending signals to other applications in PowerShell, so you may well use /usr/bin/env -sto achieve this. Note that kill is already an alias in PowerShell to Stop-Process. You need to use the full path to bypass the alias.

Suppress outputs

May commands and function calls in PowerShell have output. You might be caught in surprise sometimes because certain cmdlets or functions do have return value (i.e. output), such as New-Item:

PS X:\Scripts> New-Item test.txt -ItemType File


    Directory: X:\Scripts

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----           2019/8/11  PM 4:25              0 test.txt

If you forgot to collect the return value into a variable, or to discard them, it may be printed out, or worse, when you are writing a function, it will become one of the function’s return value. For example

function collectItems([string[]] $Items) {
    $collected = [System.Collections.ArrayList]::new()
    $collected.AddRange($Items)
    foreach ($i in $Items) {
        $collected.Add($i.Substring(0, 3))
    }
    return $collected
}

$items = collectItems(@("Alpha", "Beta", "Gamma"))
Write-Host $items.Length

What result do you expect from console? 6?

No. It’s 9. You can try it yourself, guess why, and propose a fix.

To Suppress the output of a cmdlet or an external command, you may use one the following snippets

git fetch --prune | Out-Null
New-Item ./log -Type Directory -Force > $null
$null = $hashset.Add(100)
[void](Resolve-Path ../.git)

Is Out-Null really that slow?

According to this SO post, you might want to think twice before using Out-Null because it has some overhead. However, the overhead seems relatively small on my PC. I used the following test script on 3 different environments

function Measure-Repeated([scriptblock]$Script) {
    $Rep = 1000
    $sw = [System.Diagnostics.Stopwatch]::StartNew()
    for ($i = 0; $i -lt $Rep; $i++) {
        &$Script
    }
    return $sw.Elapsed
}

function MeasureBlackholes() {
    Measure-Repeated {$(1..10000) | Out-Null}
    Measure-Repeated {$(1..10000) > $null}
    Measure-Repeated {$null = $(1..10000)}
    Measure-Repeated {[void]$(1..10000)}
}

MeasureBlackholes | % { $_.ToString() }

The result is as follows.

PowerShell 5.1 on Windows pwsh-preview 7.0-pre2 on Windows pwsh 6.2.2 on Ubuntu
00:00:17.8234508
00:00:00.3715146
00:00:00.6938085
00:00:00.7237914
00:00:00.6631330
00:00:00.3518360
00:00:00.5495256
00:00:00.5452132
00:00:02.6287777
00:00:01.9599901
00:00:02.5183607
00:00:02.5390679

It can be observed that on PowerShell, Out-Null is 48x slower than > $null, but on PowerShell Core, the gap is only 2x. It’s less performant, but not that slow.

And I love how it looks when using Out-Null. (Shhhhh)

[Keep updating!]

 

Leave a Reply

Your email address will not be published. Required fields are marked *

ERROR: si-captcha.php plugin: GD image support not detected in PHP!

Contact your web host and ask them to enable GD image support for PHP.

ERROR: si-captcha.php plugin: imagepng function not detected in PHP!

Contact your web host and ask them to enable imagepng for PHP.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Content is available under CC BY-SA 3.0 unless otherwise noted.