Ik kan niet geloven dat PowerShell echt goed is
Door Corbin Davenport 21 oktober 2026
Microsoft maakt veel slechte software. Dat omvat vele aspecten van Windows, van de primaire e-mailapplicatie die een web-wrapper is tot de alomtegenwoordige Copilot-knoppen. Daarom is het altijd een verrassing wanneer ik een Microsoft-product vind dat mijn leven op een bepaalde manier verbetert, zeker als het iets is dat voortkomt uit Windows.
Ik heb nooit veel aandacht besteed aan PowerShell, de command-line shell en scriptingtool die in 2006 verscheen en met de release van Windows 7 een gebundelde applicatie werd. In de periode dat ik Windows als mijn belangrijkste desktopbesturingssysteem gebruikte, hield ik me voornamelijk bij de Command Prompt en Batch-scripts, of Bash in WSL of Python voor complexere taken. De moderne versie van PowerShell is cross-platform en kan ook worden geïnstalleerd op Mac en Linux.
Begin dit jaar moest ik PowerShell-scripting leren voor een Windows-gerelateerd project, en ik heb het sindsdien een paar keer op Mac uitgeprobeerd. Het is snel een van mijn favoriete tools voor automatisering en scripting geworden—voor taken die ik anders zou hebben geprobeerd te doen met Node.js, Shortcuts, macOS Automator, of, god verhoede, Bash-scripts.
Ik begrijp het nu. PowerShell is geweldig, zelfs als je het niet als je primaire shell gebruikt en zelfs als je op Mac of Linux werkt.
Met het hoofd tegen de muur slaan
De meest gebruikelijke manier om een reeks commando's uit te voeren op Linux, macOS en andere Unix-gebaseerde omgevingen is met een Bash-script. Deze worden ook shell-scripts genoemd, hoewel ze technisch gezien niet hetzelfde zijn, omdat ze ook worden gebruikt door andere shells zoals Z Shell (zsh).
Hier is een voorbeeld van een Bash-script dat vraagt om een media-URL en vervolgens de applicatie yt-dlp gebruikt om deze als MP3-audiobestand op te slaan in je Desktop-map:
#!/bin/bash
echo -e "Enter a URL: "
read url
cd ~/Desktop
yt-dlp -x --audio-format mp3 "$url"
echo "Done!"
Bash-scripts zijn prima voor het simpelweg uitvoeren van een reeks commando's. Je kunt variabelen en lussen gebruiken, code die je vaker gebruikt kan in een functie worden geplaatst, en er is zelfs beperkte ondersteuning voor datatypen zoals arrays. Ze werken soms ook op een grote verscheidenheid aan hardware en besturingssystemen.
Voor alles wat iets complexer is, is Bash-scripting echter verschrikkelijk. Er zijn bijna geen "ingebouwde" functies; je wordt geacht POSIX-commando's te gebruiken voor veelvoorkomende taken, zoals bestandoperaties of het parsen van tekst. Alles daarbuiten is afhankelijk van externe tools, zoals cURL of Wget voor het ophalen van webinhoud, verschillende applicaties voor toegang tot het klembord, en jq voor het lezen van JSON-data. Ook heb je vaak boilerplate-code nodig voor het parsen van opties en parameters.
Daarnaast is het verrassend moeilijk om een Bash-script te maken dat betrouwbaar werkt op computers van anderen. Sommige mensen proberen je script in een andere shell die scripts niet op dezelfde manier interpreteert—dat is de reden waarom het commando voor het installeren van Homebrew specifiek de bash-shell aanroept, en veel andere tools dezelfde truc gebruiken. Iemand heeft misschien alleen cURL geïnstalleerd voor het ophalen van webinhoud, of alleen Wget, of beide, of geen van beide! Je kunt er niet vanuit gaan dat elk systeem dezelfde basisset aan POSIX-commando's heeft, of zelfs het sudo-commando, zoals ik een paar maanden geleden leerde.
De beperkingen van Bash-scripting duwden me meestal richting echte programmeertalen, zelfs voor taken die dat strikt genomen niet nodig hadden. Python is wellicht het beste alternatief, maar het gebrek aan accolades maakt me verdrietig. Ik schrijf veel JavaScript-code, dus Node.js is een optie, maar ik moet overal await-operators plaatsen voor systeemoperaties en hopen dat ik geen malware installeer wanneer er meer dependencies nodig zijn.
Hallo, PowerShell
Net als veel andere producten van Microsoft (en Google) is PowerShell eigenlijk twee verschillende dingen met vergelijkbare namen. De versie die je moet gebruiken is PowerShell 7, ook wel PowerShell Core genoemd, wat de moderne cross-platform versie is gebaseerd op .NET Core. De andere is Windows PowerShell (v5.1 en lager), die is gebaseerd op .NET Framework en niet meer actief wordt ontwikkeld. Windows 11 bevat standaard nog steeds alleen Windows PowerShell en niet PowerShell 7.
PowerShell 7 is gratis te downloaden voor Windows, Mac en Linux. Op mijn Mac-computers gebruikte ik Homebrew om het te installeren, waarna ik het in de Terminal kan uitvoeren met pwsh.
Het basisprincipe van een PowerShell-script verschilt niet veel van een Bash-script. Het is een lijst met commando's, geleverd door de shell of het onderliggende systeem, met de mogelijkheid om aanvullende functies en trefwoorden (zoals if, foreach of try/catch) te gebruiken waar nodig.
PowerShell heeft veel ingebouwde commando's, genaamd "cmdlets" (uitgesproken als "command-lets"), en je kunt er meer installeren of zelf maken. De namen van de meeste cmdlets zijn werkwoord-zelfstandig naamwoord-paren, zoals Set-Location in plaats van het POSIX-commando cd, Clear-Host in plaats van cls, en Remove-Item in plaats van rm.
Hier is diezelfde MP3-downloader, maar nu als een PowerShell-script:
Param(
[Parameter(Mandatory = $true)][uri]$url
)
Set-Location -Path ~/Desktop
yt-dlp -x --audio-format mp3 "$url"
Write-Host "Done!"
Het interessante deel van dit script is het Param-blok, dat gebruikmaakt van de ingebouwde parameterondersteuning van PowerShell om "url" te definiëren als een verplichte URI-invoer. PowerShell vraagt om die URL wanneer het script start (vergelijkbaar met het Bash-voorbeeld), terwijl het tegelijkertijd een -url parameter op het script toestaat als alternatieve invoedmethode.
Je kunt zelfs het ingebouwde Get-Help-commando van PowerShell op het script uitvoeren, wat de lijst met parameters en hun datatypen toont, precies zoals Get-Help dat zou doen voor een ingebouwd commando. Dezelfde functionaliteit in Bash zou veel boilerplate-code vereisen met getparams en diverse if-statements. PowerShell regelt dit allemaal voor je.
Mocht je je afvragen: het Unix-stijl ~/Desktop pad werkt in PowerShell op alle platformen. Hoewel Windows op systeemniveau paden met backslashes gebruikt, kan PowerShell deze naar behoefte converteren—native systeemcommando's doen dat echter niet, tenzij je eerst de Convert-Path cmdlet gebruikt. Net als in Bash is het tilde-teken (~) een alias voor de home-directory, of je kunt de $HOME variabele gebruiken die PowerShell op alle platformen ondersteunt.
PowerShell heeft native ondersteuning voor integers, strings, arrays en hash-tabellen. Dat is vooral nuttig bij het parsen van inhoud van web-API's, omdat PowerShell kan converteren van en naar JSON. Het is bovendien volledig objectgeoriënteerd, waardoor je custom objects kunt maken of .NET-functies kunt aanroepen indien nodig.
Je kunt elke teksteditor gebruiken om PowerShell-scripts te schrijven, net als bij Bash. Als je Visual Studio Code gebruikt, biedt dit onder andere syntax-aanvullingen, definition-tracking en linting-ondersteuning voor PowerShell.
De proefperiode
Mijn eerste echte project met PowerShell was Just the Browser, een project dat AI-functies, tracking en andere ongewenste functionaliteit uit desktop-webbrowsers probeert te verwijderen. De installer voor Mac en Linux is een Bash-script, dus het was logisch om de Windows-versie als een PowerShell-script te maken.
De taak van de installer is om te controleren welke browsers aanwezig zijn op de computer en vervolgens voor elke browser een menu-item "Update settings" te tonen. Wanneer dat item wordt geselecteerd, downloadt en installeert het script een Windows-registrybestand dat de browserinstellingen bevat. Als de instellingen zijn geïnstalleerd, toont het script een extra optie "Remove settings" in het menu om het proces ongedaan te maken.
Dit werkt door een lijst-object te maken met een .NET List<T> Class, waarbij elk lijstitem een object is dat een menulabel en een update/remove-functie bevat. Om te controleren of bepaalde registry-vermeldingen al zijn gewijzigd, gebruikte ik de Test-Path en Get-ItemProperty cmdlets, als volgt:
if (Test-Path "HKLM:\SOFTWARE\Policies\Google\Chrome") {
$IfAiSettingExists = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Google\Chrome").AIModeSettings
}
Er zijn een paar interessante zaken in dit fragment. Je merkt misschien dat het registry-pad meer lijkt op een bestandspad op een schijf genaamd "HKLM" dan op de gebruikelijke Windows Registry-hiërarchie, die er zo uitziet: HKEYLOCALMACHINE\SOFTWARE\Policies\Google\Chrome.
PowerShell maakt een virtuele schijf aan voor het Windows Registry, waardoor je registry-sleutels kunt ophalen, toevoegen, wijzigen, wissen en verwijderen alsof het bestanden in een bestandssysteem zijn. Diezelfde Test-Path cmdlet zou ook werken om te controleren of een bepaald bestand in mijn Downloads-map staat. Je hoeft niet apart te leren hoe je bestanden en registry-sleutels beheert, want in PowerShell zijn ze hetzelfde.
Er zijn andere virtuele schijven in PowerShell, zoals die voor de certificaatopslag in Windows, en je kunt zelfs je eigen schijven maken. Als je het beu bent om een lang pad in je shell of scripts te typen, maak dan een virtuele schijf die ernaartoe wijst. Je kunt het zelfs FART:\ noemen als je wilt.
Dat script moet ook de .reg-bestanden downloaden voor het configureren van webbrowsers. Hier is een vereenvoudigde versie van het proces voor Google Chrome:
$GoogleChromeInstallRegistry = "https://raw.githubusercontent.com/corbindavenport/just-the-browser/main/chrome/install.reg"
Invoke-WebRequest $GoogleChromeInstallRegistry -OutFile "$env:LocalAppData\chrome.reg"
$ChromeInstall = Start-Process "reg.exe" -ArgumentList "import `"$env:LocalAppData\chrome.reg`"" -WindowStyle Hidden -Wait -PassThru
Dit maakt gebruik van de Invoke-WebRequest cmdlet om het registry-bestand te downloaden van de opgegeven URL en slaat dit tijdelijk op in de AppData-map van het gebruikersaccount. Vervolgens start het het reg.exe commando in een verborgen venster, vertelt dit om het zojuist gedownloade bestand te importeren, en wacht tot het commando is voltooid. PowerShell zou de sleutels direct kunnen wijzigen, maar ik wilde dat ze in een apart bestand stonden dat handmatig kan worden geïmporteerd, aangezien niet iedereen de geautomatiseerde installer wil gebruiken.
De Invoke-WebRequest cmdlet werkt als Wget of cURL op een Mac- of Linux-systeem, waardoor je webinhoud in je script kunt downloaden. Dat is een andere functie die in PowerShell op alle platformen is ingebouwd en die ik in Bash-scripts vaak nodig heb.
(Noot: Eerdere versies van Windows PowerShell bevatten een alias voor het curl-commando die doorverwees naar Invoke-WebRequest. Deze ondersteunde niet alle functies van de echte cURL, wat leidde tot bugrapporten voor het daadwerkelijke cURL-project. Die alias is verwijderd in PowerShell 7, en Windows bevat nu de echte applicatie.)
Ik wilde dat de installer ook werkte in oudere versies van Windows PowerShell, zodat deze op oudere pc's kon draaien zonder eerst de PowerShell-versie te hoeven upgraden. Dat vereiste slechts enkele aanpassingen, zoals dit commando dat TLS v1.2 inschakelt voordat Invoke-WebRequest wordt gebruikt, omdat dit vroeger niet standaard was ingeschakeld:
[Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12
Voor een ander project experimenteerde ik met het samenvoegen van mijn oude Tumblr-blog in mijn persoonlijke website, die gebruikmaakt van de Eleventy static site generator. Er waren geen bestaande tools hiervoor, dus ik maakte een "Tumblr to Eleventy" PowerShell-script.
Het script heeft twee belangrijke inputs: de archiefexport van Tumblr (een ZIP-bestand met HTML en media-bijlagen) en de Tumblr API. Het gebruikt Invoke-WebRequest en ConvertFrom-Json om de API te controleren en door alle berichten op de blog te lopen, Expand-Archive om het ZIP-bestand uit te pakken, en cmdlets zoals Copy-Item en New-Item om een nieuwe mappenstructuur te maken die klaar is voor een Eleventy-site.
Het belangrijkste onderdeel was het behouden van alle metadata van het oorspronkelijke Tumblr-bericht, inclusief de titel, permalink, publicatiedatum en tags. Het script doet dit door een custom object te maken met alle data, en dit vervolgens met de ConvertTo-Json cmdlet om te zetten naar een Eleventy-vriendelijk JSON-formaat:
$OutputJson = Join-Path -Path $OutputPath -ChildPath "index.json"
$PostMetadata = [PSCustomObject]@{
title = $Post.title ? $Post.title : $Post.summary
permalink = ($Post.post_url -replace $Post.blog.url, "/") + "/index.html"
date = [DateTimeOffset]::FromUnixTimeSeconds($Post.timestamp).UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ssZ")
tags = $tag ? (@($tag) + $Post.tags) : $Post.tags
tumblr_id = $Post.id
tumblr_url = $Post.post_url
tumblr_short_url = $Post.short_url
tumblr_blog_name = $Post.blog_name
}
$PostMetadata | ConvertTo-Json -Depth 1 | Set-Content -Path $OutputJson -Encoding UTF8
De $Post variabele bevat de respons van de Tumblr API; dit is dus simpelweg het mappen van die data naar de keys die Eleventy verwacht. De API plaatste bijvoorbeeld soms mijn post-titels als de samenvatting in plaats van de titel, dus het script controleert eerst .title en gebruikt .summary als fallback. Tumblr leverde de publicatiedatum en -tijd van elk bericht in Unix-tijd, wat PowerShell kan converteren naar het UTC-formaat dat vereist is door Eleventy.
Ik zou dit waarschijnlijk met Node.js hebben gedaan als het niet PowerShell was geweest, aangezien Node ingebouwde web-requests en bestandshandling heeft. Echter, dat zou externe dependencies hebben vereist voor eenvoudige taken zoals het uitpakken van het archief, evenals await-statements op talloze plekken. Ik ben uiteindelijk mijn blog niet verplaatst, maar hopelijk vindt iemand anders dat script nuttig.
Nog een tool in de gereedschapskist
Ik ben nog steeds bezig met het onder de knie krijgen van PowerShell-scripting, maar na elk project ben ik meer onder de indruk. Het is cross-platform, voorspelbaarder en herbruikbaarder dan Bash-scripts, en relatief eenvoudig te begrijpen. Ik kan niet geloven dat dit uit hetzelfde bedrijf komt als Copilot.
Natuurlijk doet dit er niet toe als je scripting en automatisering op de "hippe" manier doet, met Claude of een andere AI-chatbot aan het stuur. De bot kan het denken en testen voor je doen. Ik hou echter van leren en ik doe mijn best om te voorkomen dat mijn brein atrofieert. Een lokaal Gemma 4-model in LM Studio hielp me wel bij het oplossen van problemen wanneer webzoekopdrachten faalden, maar het overgrote deel van mijn scripting is tot nu toe met liefde handmatig geschreven.
Als je geïnteresseerd bent in leren hoe je PowerShell gebruikt, zijn er genoeg online handleidingen en videotutorials. Ik heb "PowerShell 7 for Beginners" van JackedProgrammer gekeken, en misschien schaf ik op een gegeven moment zelfs een referentieboek aan. Dat kan dan mooi naast mijn paperback-editie uit 1996 van O'Reilly's HTML: The Definitive Guide staan.
Groetjes,