meta package for our recommended PowerShell plugins and settings

To update (replacing the current version) run webi pwsh-essentials.

Cheat Sheet

The tools you need to write PowerShell effectively.

This meta package will install the full set of plugins and settings we recommended.

Table of Contents

  • Files
  • Mostly Case-Insensitive
  • Returns vs Pipeline Streams
  • Strict, Trace, & Verbose Modes
  • The Call Operator "&"
  • curl vs curl.exe
  • Script Policies & Preferences

Files

These are the files / directories that are created and/or modified with this install:

~/.config/envman/PATH.env
~/.local/bin/pwsh-fix.ps1
~/.local/bin/pwsh-fmt.ps1
~/.local/bin/pwsh-lint.ps1
~/.local/opt/pwsh/
~/.local/share/powershell/Modules/PSScriptAnalyzer/

Things You MUST Know

There are a few key differences to PowerShell from other scripting and programming languages you may have used in the past.

Knowing these from the start can save you a tonne of headache.

0. PowerShell is Case-Insenstive, Usually

Function and Commandlet names, file paths, and boolean operators are all case-insensitive:

write-host "Hello, World!"
WRITE-HOST "Hello, World!"

Write-Host "HELLO, WORLD!" -eq "hello, world!"
# True

However, raw string and byte functions are case sensitive unless 'CurrentCultureIgnoreCase' is used:

"https://webi.sh".StartsWith("HTTPS://")
# False

"https://webi.sh".StartsWith("HTTPS://", 'CurrentCultureIgnoreCase')
# True

1. PowerShell Doesn't Have Return Values

There's not so much concept of a "return" as a "pipeline stream".

All values which are not assigned to a variable, such as $Foobar - or $null - are pipelined.

  • There are no Return Values
  • The Return keyword only serves to exit early.
  • The result of EVERY command is put into a Pipeline
  • You can pick which Pipeline a result is written to:
    • ... | Write-Output (the default)
    • ... | Write-Debug
    • ... | Write-Verbose
    • ... | Write-Information
    • ... | Write-Warning
    • ... | Write-Error
    • ... | Write-Host (forced console output, no Pipeline)
      (also, considered "evil")
  • Results captured with a variable DO NOT enter a pipeline
    • $Foobar = ... (captured to variable, no Pipeline)
    • $null = ... (no Pipleline)
      (the same as ... | Out-Null, but easier to read and much faster)

Example

function Get-LotsInThePipeline ($Thing1, $Thing2) {
    Write-Output "a"
    1

    IF ($Thing) {
        $null = Write-Output "b"
        $null = 2
        $Thing2
        Return
    }

    Write-Output "c"
    3
}

Get-LotsInThePipeline $true 'red'
Get-LotsInThePipeline $false 'blue'

There are two possible outputs for this program:

a, 1, "red"
a, 1, c, 3

When you need a limited set of pipeline values, you'll need to $null = xxxx... a lot.

See https://stackoverflow.com/q/29556437/151312

2. Strict, Trace, & Verbose Modes

# set -e
$ErrorActionPreference = "Stop"

# set -x
Set-PSDebug -Trace 2

# DEBUG='true'
$VerbosePreference="Continue"
# if test -n "$DEBUG"; then echo "Debug 123"; fi
Write-Verbose "Debug: 123"

3. The Call Operator (&)

Normally you can run a command the same as a commandlet:

# Built-in Commandlet
Write-Host "Unpacking the tarball..."

# External Command
tar xvf foobar.tar.gz

However, if the command is any of:

  • a script (particular that will change its parent's variables)
  • accessed directly by it's path (i.e. it's not in $Env:Path)
  • in a path with a space

you must use the call operator:

# can't write parent's variables
pwsh -ExecutionPolicy Bypass $HOME\.local\bin\_webi.ps1 xz

# can write parent's variables (same process)
& $HOME\.local\bin\_webi.ps1 xz

# not in PATH, and has space in its path
& ".\Foo Bar\foobar.exe" -baz

To use it with multiple arguments:

# this can be just the name, or the full path
$Cmd = 'curl.exe'

# mind the leading ,
$CmdArguments = , '--fail-with-body', '-sS'

& $Cmd $CmdArguments -o example.html 'https://example.com'

As you can see, the array of options and arguments is flattened, which makes constructing commands with complex arrangements much easier than... posix (but don't tell my friends I said that).

4. PowerShell's curl vs Windows' curl.exe

PowerShell has a built-in curl which is an alias for the Invoke-WebRequest commandlet.

Never use curl. Always use curl.exe.

5. PowerShell Preferences and Policies

  • Execution Policy must be set to allow PowerShell to run scripts:
    # from a shell
    powershell -ExecutionPolicy Bypass .\foobar.ps1
    
    # for a script, itself
    Set-ExecutionPolicy Bypass -Scope Process
    & .\foobar.ps1
    
  • Set Strict Mode (like set -e in Bash, etc)
    $ErrorActionPreference = 'Stop'
    
  • Eliminate Progress Bars (they really slow Windows down) \
    $ProgressPreference = 'SilentlyContinue'
    
    The number of progress created by Invoke-WebRequest during a download is so many that it literally can't download files over a few kilobytes in a reasonable amount of time unless this is turned off.

Contribute

Report an Issue Submit Installer Star on GitHub