Start Windows Terminal in WSL2 home directory

A little tip, partly written for the benefit of future-me…

As you may know, WSL2 filesystem performance is much improved over its predecessor – but only in the virtual disk of your Linux distro. The mounted Windows disk – /mnt/c/ – is still slow for disk-heavy operations, like Git clones, because of Linux-plus-NTFS something reasons.

Because of this, the first thing I do when I open my WSL2 Ubuntu distro in Windows Terminal is change to my Linux home directory. Every time.

There must be an easier way? Yep.

From the WSL docs:

bash ~ launches the bash shell into the user’s home directory. Similar to running cd ~.

To make this the default in Windows Terminal, open the Windows Terminal Settings, find your WSL2 profile, and add “commandline”: “bash.exe ~”

{
    "guid": "{2c4de342-38b7-51cf-b940-2309a097f518}",
    "hidden": false,
    "name": "Ubuntu",
    "source": "Windows.Terminal.Wsl",
    "commandline": "bash.exe ~"
}

(Note that the “source” line now needs a trailing comma.)

From this:

To this:

View all Windows Terminal colour schemes

Want to set the snazziest Windows Terminal colour scheme but not sure how to go about it?

Surely this is the top of everyone’s to-do list at this time of unprecedented calmness and serenity…

You could spend 5 minutes manually trying out the various options.

You could spend an hour hacking together a fairly rickety script that launches Windows Terminal with each of the built-in schemes.

Or you could let me do that for you and spend 20 seconds watching the results in video form:

Why not reward yourself for your time efficiency by spending 4 minutes 40 seconds scouring the Internet for Coronavirus morsels? Some suggested search terms:

  • potato long term storage tips
  • discourage social interaction clothing ideas obscene
  • maximum safe potato consumption
  • handshake alternatives obscene
  • where buy potato minneapolis

Stay safe out there.

Here’s the code:

# Script that cycles through all Windows Terminal color schemes
# Depends on there being a profile called "PowerShell" with a colorScheme already set
param(
    $profilesJsonPath = (Join-Path $env:LOCALAPPDATA 'Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\profiles.json'),
    $defaultsJsonPath = (Join-Path $env:ProgramFiles 'WindowsApps\Microsoft.WindowsTerminal_0.10.761.0_x64__8wekyb3d8bbwe\defaults.json')
)

# Get all color schemes from default profiles file
$defaults = Get-Content $defaultsJsonPath | ConvertFrom-Json
$colorSchemes = $defaults.schemes.name

# Get profiles as text
$profiles = Get-Content $profilesJsonPath

# Fragile string stuff to find index of the colorScheme line in the PowerShell profile
# Easier than dealing with JSON + comments as objects?
$psNameIndex = ($profiles | Select-String '"name": "PowerShell",').LineNumber - 1
$sectionBoundaries = ($profiles | Select-String '},').LineNumber | ForEach-Object {$_ - 1} # Section delimited by '},'
$sectionStart = ($sectionBoundaries | Where-Object {$_ -lt $psNameIndex})[-1]
$sectionEnd = ($sectionBoundaries | Where-Object {$_ -gt $psNameIndex})[0]
$colorIndex = ($profiles[$sectionStart..$sectionEnd] | Select-String '"colorScheme":').LineNumber - 1 + $sectionStart

ForEach ($newScheme in $colorSchemes) {
    # Regex to insert new scheme
    $profiles[$colorIndex] = $profiles[$colorIndex] -replace '"colorScheme":.*[^,]' ,"`"colorScheme`": `"$newScheme`""
    # Write out JSON
    $profiles | Out-File $profilesJsonPath
    # Launch Windows Terminal
    $command = "Read-Host `"$newScheme`""
    $arguments = @{
        'FilePath' = 'wt.exe'
        'ArgumentList' = "-p `"PowerShell`" pwsh.exe -Command $command"
        'Wait' = $true
        'WindowStyle' = 'Maximized'
    }
    Start-Process @arguments
}

Add PowerShell 7-preview to Microsoft Windows Terminal (0.6+)

Update: the Windows Terminal now automatically detects PowerShell Core and this blog post is defunct.

The new Windows Terminal continues to be developed and it has a dwindling list of deal-breakers (why won’t my mousewheel scroll??)

Along the way, the format of the settings file has changed, and my previous method for adding a profile for PowerShell 7 no longer works.

Below is a new block of code that’ll do the job. As before, just copy-n-paste into a PowerShell window and you’re good to go.

PowerShell 7-preview x64 needs to be installed and you need to have run Windows Terminal at least once.

I’m now doing horrible things with strings – because it’s easier than dealing with a comments and a JSON object – so this code is officially 3x more likely to blow up.

# Get Windows Terminal settings
$terminalFolderPath = "$env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState"
$settingsFilePath = Join-Path $terminalFolderPath 'profiles.json'
[System.Collections.ArrayList]$settings = Get-Content $settingsFilePath
# Download icon
$pwsh7IconPath = Join-Path $terminalFolderPath 'pwsh7.ico'
Invoke-WebRequest -Uri 'https://raw.githubusercontent.com/weebsnore/Add-PS7ToWindowsTerminal/master/pwsh7.ico' -OutFile $pwsh7IconPath
# Generate PS7 profile JSON
$ps7profile = @{
    'guid' = '{' + (New-Guid).ToString() + '}'
    'name' = 'PowerShell 7-preview (x64)'
    'commandline' = 'C:\Program Files\PowerShell\7-preview\pwsh.exe'
    'icon' = $pwsh7IconPath
} | ConvertTo-Json
# Append comma to profile JSON
$ps7profile = $ps7profile + ','
# Find "profiles" line number
$profilesLine = ($settings | Select-String '"profiles":').LineNumber
# Add new profile to JSON and write to disk
,$settings.Insert($profilesLine+1,$ps7profile)
$settings | Out-File $settingsFilePath

Code here on GitHub.

Add PowerShell-7 preview to Windows Terminal in 30 seconds

Edit: The stuff below doesn’t work any more. But this new stuff works.

Fun times in Windows land over the last few days with the release of new previews of Windows Terminal (v0.4) and PowerShell 7 (Preview 3).

To celebrate, here’s a simple script that you can copy-and-paste into a PowerShell window to add PowerShell 7 to Windows Terminal. (You can even use the Windows Terminal PowerShell terminal. Wow!)

The whole thing might take 30 seconds, which is a significant improvement on my last effort of 2 minutes. Why not take 1 minutes 30 seconds to make yourself a drink?

Pre-requisites:

  • PowerShell 7-preview x64 is installed
  • Windows Terminal v0.4 is installed and has been run

Here’s what it looks like:

Here’s the code:

$terminalFolderPath = "$env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState"
# Get Windows Terminal settings file
$settingsFilePath = Join-Path $terminalFolderPath 'profiles.json'
$json = Get-Content $settingsFilePath | ConvertFrom-Json
# Get profiles
$profiles = $json.profiles
# Make a copy of first profile and configure for PS7 x64
$ps7 = $profiles[0].psobject.Copy()
$ps7.name = 'PowerShell 7-preview (x64)'
$ps7.commandline = 'C:\Program Files\PowerShell\7-preview\pwsh.exe'
$ps7.guid = '{' + (New-Guid).ToString() + '}'
# Download and set icon
$pwsh7IconPath = Join-Path $terminalFolderPath 'pwsh7.ico'
Invoke-WebRequest -Uri 'https://raw.githubusercontent.com/weebsnore/Add-PS7ToWindowsTerminal/master/pwsh7.ico' -OutFile $pwsh7IconPath
$ps7.icon = $pwsh7IconPath
# Write updated settings file to disk
$json.profiles = $profiles + $ps7
$json | ConvertTo-Json | Out-File $settingsFilePath

And here it is on GitHub.

Tip: avoid Wait-Debugger gotcha on Azure Functions

My biggest gotcha with Azure Functions is that you need to put Wait-Debugger in your script for local debugging, and it’s easy to forget to remove it when you deploy to Azure.

My last post talked about exploring Azure Functions’ environment, and I mentioned that you could compare the cloud version with its locally-run approximation.

Well, when you’re running your function locally, the variable $env:AZURE_FUNCTIONS_ENVIRONMENT is set to Development.

This means you can ensure that you never leave a function hanging at Wait-Debugger in the cloud by wrapping it like this:

if ($env:AZURE_FUNCTIONS_ENVIRONMENT -eq 'Development') {
    Wait-Debugger
}

Another solution is to wrap your debug command like this:

if ($Request.Query.Debug -eq 'True') {
    Wait-Debugger
}

And then invoke your function with &Debug=True when you want to debug it.

How do you handle this problem?