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 -s
to 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!]