Skip to content
HN On Hacker News ↗

I can't believe PowerShell is actually good

▲ 76 points • 92 comments • by corbindavenport • 2w ago • HN discussion ↗

Pangram verdict · v3.3

We believe that this entire text is human-written.

0 %

AI likelihood · overall

Human
100% human-written 0% AI-generated
SEGMENTS · HUMAN 1 of 1
SEGMENTS · AI 0 of 1
WORD COUNT 1,409
PEAK AI % 0% · §1
Analyzed
Oct 22
backend: pangram/v3.3
Segments scanned
1 windows
avg 1409 words each
Distribution
100 / 0%
human / AI fraction
Verdict
Human
Pangram v3.3

Article text · 1,409 words · 1 segments analyzed

Human AI-generated
§1 Human · 0%

Microsoft makes a lot of bad software. That includes many aspects of Windows, from the primary email application being a web wrapper to the ever-present Copilot buttons. As such, it's always a surprise to me when I find a Microsoft product that improves my life in some respect, especially when it's something that came from Windows.I never paid much attention to PowerShell, the command-line shell and scripting tool that first arrived in 2006, and became a bundled application with the release of Windows 7. For the time period I used Windows as my main desktop operating system, I mostly stuck to the Command Prompt and Batch scripts, or Bash in WSL or Python for more complex tasks. The modern version of PowerShell is cross-platform, and can also be installed on Mac and Linux.Earlier this year, I needed to learn PowerShell scripting for one Windows-related project, and I have tried it out a few more times on Mac. It has quickly become one of my favorite tools for automation and scripting—taking on jobs that I would have otherwise tried to do with Node.js, Shortcuts, macOS Automator, or heaven forbid, Bash scripts.I get it now. PowerShell is great, even if you don't use it as your shell, and even if you're on Mac or Linux.Bashing my head against the wallThe most common way to run a sequence of commands on Linux, macOS, and other Unix-based environments is with a Bash script. They are also called shell scripts, even though they're not technically the same thing, because they are also used by other shells like Z Shell (zsh).Here's an example Bash script that asks you for a media URL, and then uses the yt-dlp application to save it as an MP3 audio file to your Desktop folder:#!/bin/bash echo -e "Enter a URL: " read url cd ~/Desktop yt-dlp -x --audio-format mp3 "$url" echo "Done!"Bash scripts are fine for just running a series of commands. You can use variables and loops, code you use more than once can go in a function, and there's even limited support for data types like arrays. They can also sometimes work on a wide variety of hardware and operating systems.For anything slightly more complicated, Bash scripting is just the worst. There are almost no "built-in" functions—you are supposed to use POSIX commands for common tasks, like file operations or parsing text. Anything beyond that depends on external tools, like cURL or Wget for retrieving web content, several different applications for accessing the clipboard, and jq for reading JSON data. You might also need boilerplate code for parsing options and parameters.It is also surprisingly difficult to make a Bash script that works reliably on other people's computers. Some people might try your script in a different shell that doesn't interpret scripts in the same way—that's why the command for installing Homebrew starts with calling the bash shell specifically, and many other tools use the same trick. Someone might only have cURL installed for fetching web content, or only Wget, or both, or neither! You can't assume every system will have the same basic set of POSIX commands, or even the sudo command, as I learned a few months ago.The limitations of Bash scripting usually pushed me towards actual programming languages, even for tasks that didn't strictly need them. Python might be the best alternative, but its lack of curly braces makes me sad. I write a lot of JavaScript code, so Node.js is an option, but I have to put await operators everywhere for system operations and hope that I'm not installing malware when more dependencies are needed.Hello, PowerShellLike many other products from Microsoft (and Google), PowerShell is actually two different things with similar names. The one you should use is PowerShell 7, also called PowerShell Core, which is the modern cross-platform version based on .NET Core. The other one is Windows PowerShell (v5.1 and below), which is based on .NET Framework and not actively developed. Windows 11 still only includes Windows PowerShell, and not PowerShell 7.PowerShell 7 is a free download on Windows, Mac, and Linux. On my Mac computers, I used Homebrew to install it, and then I can run it with pwsh in the Terminal.The basic premise of a PowerShell script isn't much different than a Bash script. It's a list of commands, either provided by the shell or the underlying system, with the ability to use additional functions and keywords (like if, foreach, or try/catch) as needed.PowerShell has many built-in commands, called "cmdlets" (pronounced "command-lets"), and you can install more or make your own. The names for most of them are verb-noun pairs, like Set-Location instead of POSIX's cd, Clear-Host instead of cls, and Remove-Item instead of rm.Here's that MP3 downloader again, but now as a PowerShell script:Param( [Parameter(Mandatory = $true)][uri]$url ) Set-Location -Path ~/Desktop yt-dlp -x --audio-format mp3 "$url" Write-Host "Done!"The cool part in that script is the Param block, which uses PowerShell's built-in parameter support to define "url" as a mandatory URI input. PowerShell will ask for that URL when the script starts, similar to the Bash example, while also allowing a -url parameter on the script as an alternative input method.You can even run PowerShell's built-in Get-Help command on the script, which shows the list of parameters with their data types, just like Get-Help would for a built-in command.This same functionality in Bash would need a lot of boilerplate code with getparams and various if statements. PowerShell handles all that for you.In case you were wondering, that Unix-style ~/Desktop path works in PowerShell on all platforms. Even though Windows uses paths with backslashes at the system level, PowerShell can convert them as needed—native system commands might not, though, unless you use the Convert-Path cmdlet first. Just like in Bash, the tilde (~) character is an alias for the home directory, or you can use the $HOME variable that PowerShell supports on all platforms.PowerShell has native support for integers, strings, arrays, and hash tables. That's especially useful when parsing content from web APIs, because PowerShell can convert to and from JSON. It's also fully object-oriented, and you can create custom objects or call .NET functions if needed.You can use any text editor to write PowerShell scripts, just like with Bash. If you happen to use Visual Studio Code, it offers syntax completions, definition tracking, and linting support for PowerShell, among other features.The trial runMy first real project with PowerShell was Just the Browser, a project that aims to remove AI features, tracking, and other unwanted functionality from desktop web browsers. The installer for Mac and Linux is a Bash script, so it made sense to make the Windows version as a PowerShell script.The installer's job is to check which browsers are present on the computer, and then show an "Update settings" menu item for each browser. When that item is selected, the script downloads and installs a Windows registry file containing the browser settings. If the settings have been installed, the script shows an additional "Remove settings" option in the menu to reverse the process.This works by creating a list object with a .NET List<T> Class, with each list item being an object that contains a menu label and update/remove function. To check if any registry entries have already been modified, I used the Test-Path and Get-ItemProperty cmdlets, like this:if (Test-Path "HKLM:\SOFTWARE\Policies\Google\Chrome") { $IfAiSettingExists = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Google\Chrome").AIModeSettings }There are a few cool things in that snippet. You might have noticed that registry path looks more like a file path on a drive called "HKLM" than the usual Windows Registry hierarchy, which looks like this:HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\ChromePowerShell creates a virtual drive for the Windows Registry, allowing you to get, add, change, clear, and delete registry keys as if they were files in any file system. That same Test-Path cmdlet would also work for checking if a certain file was in my Downloads folder. You don't have to learn how to use files and how to use registry keys, because they're the same thing in PowerShell.There are other virtual drives in PowerShell, like the one for certificate storage in Windows, and you can even create your own. If you're tired of typing a long path in your shell or scripts, make a virtual drive that points to it! You can even call it FART:\ if you want.That script also has to download the .reg files for configuring web browsers. Here's a simplified version of the process for Google Chrome:$GoogleChromeInstallRegistry = "https://raw.githubusercontent.com/corbindavenport/just-the-browser/main/chrome/install.reg" Invoke-WebRequest $GoogleChromeInstallRegistry -OutFile "$env:LocalAppData\chrome.reg"