PowerShell 为什么好用:一位开发者的实测体验
I can't believe PowerShell is good
一位长期使用 Windows、Mac 和 Linux 的开发者表示,PowerShell 7(PowerShell Core)已成为其最爱的自动化脚本工具之一,可替代 Node.js、Shortcuts、macOS Automator 甚至 Bash 脚本。
Microsoft 做了大量糟糕的软件。这包括 Windows 的许多方面,从主电子邮件应用只是一个网页套壳到无处不在的 Copilot 按钮。因此,每当我发现某个 Microsoft 产品在某种程度上改善了我的生活时,我总是很惊讶,尤其是当它来自 Windows 时。
我以前从未太关注 PowerShell,这个命令行 shell 和脚本工具最早于 2006 年推出,并随着 Windows 7 的发布成为捆绑应用。在我把 Windows 作为主要桌面操作系统的时期,我大多坚持使用命令提示符和批处理脚本,或者在更复杂的任务中使用 WSL 中的 Bash 或 Python。现代版本的 PowerShell 是跨平台的,也可以安装在 Mac 和 Linux 上。
今年早些时候,我需要为一个与 Windows 相关的项目学习 PowerShell 脚本,之后我又在 Mac 上尝试了几次。它很快成为我最喜欢的自动化和脚本工具之一——承担了我原本会尝试用 Node.js、快捷指令、macOS Automator 或者天哪,Bash 脚本来完成的工作。
我现在明白了。PowerShell 很棒,即使你不把它用作你的 shell,即使你在 Mac 或 Linux 上也是如此。
拿头撞墙
在 Linux、macOS 和其他基于 Unix 的环境中运行一系列命令的最常见方式是使用 Bash 脚本。它们也被称为 shell 脚本,尽管它们在技术上并不是同一回事,因为它们也被其他 shell 如 Z Shell(zsh)使用。
下面是一个 Bash 脚本示例,它会要求你输入媒体 URL,然后使用 yt-dlp 应用将其保存为 MP3 音频文件到你的桌面文件夹:
#!/bin/bash
echo -e "Enter a URL: "
read url
cd ~/Desktop
yt-dlp -x --audio-format mp3 "$url"
echo "Done!"Bash 脚本对于只运行一系列命令来说没问题。你可以使用变量和循环,使用多次的代码可以放在函数中,甚至对数组等数据类型也有有限的支持。它们有时也能在各种各样的硬件和操作系统上运行。
对于稍微复杂一点的事情,Bash 脚本就是最糟糕的。几乎没有“内置”函数——你应该使用 POSIX 命令来完成常见任务,比如文件操作或解析文本。除此之外的任何事情都依赖于外部工具,比如用 cURL 或 Wget 获取网页内容,用几个不同的应用访问剪贴板,以及用 jq 读取 JSON 数据。你可能还需要用于解析选项和参数的样板代码。
而且,要制作一个能在别人的电脑上可靠运行的 Bash 脚本也出奇地困难。有些人可能会在不同的 shell 中尝试你的脚本,而该 shell 对脚本的解释方式不同——这就是为什么安装 Homebrew 的命令以专门调用 bash shell 开头,许多其他工具也使用同样的技巧。有些人可能只安装了 cURL 用于获取网页内容,或者只安装了 Wget,或者两者都有,或者都没有!你不能假设每个系统都有相同的基本 POSIX 命令集,甚至不能假设有 sudo 命令,正如我几个月前学到的。
Bash 脚本的局限性通常促使我转向真正的编程语言,即使任务并不严格需要它们。Python 可能是最好的替代品,但它缺少花括号让我很难受。我写了很多 JavaScript 代码,所以 Node.js 是一个选择,但我必须在系统操作中到处使用 await 运算符,并且在需要更多依赖时祈祷自己没有安装恶意软件。
你好,PowerShell
和微软(以及谷歌)的许多其他产品一样,PowerShell 实际上也是两个不同的东西,名字相似。你应该使用的是 PowerShell 7,也叫 PowerShell Core,这是基于 .NET Core 的现代跨平台版本。另一个是 Windows PowerShell(v5.1 及以下),它基于 .NET Framework,且不再积极开发。Windows 11 仍然只包含 Windows PowerShell,而不包含 PowerShell 7。
PowerShell 7 在 Windows、Mac 和 Linux 上可免费下载。在我的 Mac 电脑上,我使用 Homebrew 安装它,然后在终端中用 pwsh 运行。

PowerShell 脚本的基本前提与 Bash 脚本没有太大区别。它是一系列命令,由 shell 或底层系统提供,并可根据需要使用额外的函数和关键字(如 if、foreach 或 try/catch)。
PowerShell 有许多内置命令,称为“cmdlet”(发音为“command-lets”),你可以安装更多或自己创建。大多数命令的名称是动词-名词对,例如用 Set-Location 代替 POSIX 的 cd,用 Clear-Host 代替 cls,用 Remove-Item 代替 rm。
还是那个 MP3 下载器,但现在是 PowerShell 脚本:
Param(
[Parameter(Mandatory = $true)][uri]$url
)
Set-Location -Path ~/Desktop
yt-dlp -x --audio-format mp3 "$url"
Write-Host "Done!"该脚本中很酷的部分是 Param 块,它使用 PowerShell 的内置参数支持将“url”定义为必需的 URI 输入。PowerShell 会在脚本启动时询问该 URL,类似于 Bash 示例,同时还允许在脚本上使用 -url 参数作为替代输入方式。
你甚至可以在脚本上运行 PowerShell 的内置 Get-Help 命令,它会显示参数列表及其数据类型,就像 Get-Help 对内置命令所做的那样。

在 Bash 中实现同样的功能需要大量样板代码,包括 getparams 和各种 if 语句。PowerShell 为你处理所有这些。
顺便说一下,那种 Unix 风格的 ~/Desktop 路径在 PowerShell 中适用于所有平台。尽管 Windows 在系统层面使用反斜杠路径,PowerShell 可以根据需要转换它们——不过原生系统命令可能不会,除非你先使用 Convert-Path cmdlet。就像在 Bash 中一样,波浪号(~)字符是主目录的别名,或者你可以使用 PowerShell 在所有平台上都支持的 $HOME 变量。
PowerShell 原生支持整数、字符串、数组和哈希表。这在解析 Web API 内容时特别有用,因为 PowerShell 可以转换到和转换自 JSON。它也是完全面向对象的,你可以创建自定义对象或在需要时调用 .NET 函数。
你可以像使用 Bash 一样,用任何文本编辑器编写 PowerShell 脚本。如果你碰巧使用 Visual Studio Code,它为 PowerShell 提供了语法补全、定义跟踪和 linting 支持等功能。
试运行
我用 PowerShell 做的第一个真正意义上的项目是 Just the Browser,这个项目旨在从桌面浏览器中移除 AI 功能、跟踪以及其他不需要的功能。Mac 和 Linux 的安装程序是一个 Bash 脚本,因此把 Windows 版本做成 PowerShell 脚本也就顺理成章了。
安装程序的任务是检查电脑上存在哪些浏览器,然后为每个浏览器显示一个“更新设置”菜单项。当选中该项时,脚本会下载并安装一个包含浏览器设置的 Windows 注册表文件。如果设置已经安装,脚本会在菜单中额外显示一个“移除设置”选项,以撤销此过程。

其工作原理是使用 .NET List<T> 类创建一个列表对象,每个列表项都是一个包含菜单标签和更新/移除函数的对象。为了检查是否有注册表项已被修改,我使用了 Test-Path 和 Get-ItemProperty cmdlet,如下所示:
if (Test-Path "HKLM:\SOFTWARE\Policies\Google\Chrome") {
$IfAiSettingExists = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Google\Chrome").AIModeSettings
}这段代码中有几个很酷的地方。你可能已经注意到,注册表路径看起来更像是一个名为“HKLM”的驱动器上的文件路径,而不是通常的 Windows 注册表层级结构,后者看起来是这样的:
HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\ChromePowerShell 为 Windows 注册表创建了一个虚拟驱动器,让你可以像操作任何文件系统中的文件一样,获取、添加、更改、清除和删除注册表项。同一个 Test-Path cmdlet 也可以用来检查某个文件是否在我的 Downloads 文件夹中。你不必同时学习如何使用文件和如何使用注册表项,因为在 PowerShell 中它们是一回事。
PowerShell 中还有其他虚拟驱动器,比如用于 Windows 中证书存储的那个,你甚至可以创建自己的虚拟驱动器。如果你厌倦了在 shell 或脚本中输入长路径,那就创建一个指向它的虚拟驱动器吧!你甚至可以把它命名为 FART:\,如果你愿意的话。
该脚本还需要下载用于配置浏览器的 .reg 文件。以下是针对 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这里使用 Invoke-WebRequest cmdlet 从提供的 URL 下载注册表文件,并将其临时存储在用户账户的 AppData 文件夹中。然后它在隐藏窗口中启动 reg.exe 命令,告诉它导入刚刚下载的文件,并等待命令完成。PowerShell 可以直接修改注册表项,但我希望将它们放在一个可以手动导入的单独文件中,因为并非每个人都想使用自动化安装程序。
Invoke-WebRequest cmdlet 的工作方式类似于 Mac 或 Linux 系统上的 Wget 或 cURL,允许你在脚本中下载 Web 内容。这是所有平台上 PowerShell 内置的另一个功能,而我在 Bash 脚本中经常需要用到它。
我希望安装程序能在旧版本的 Windows PowerShell 中运行,这样它就可以在较旧的 PC 上运行,而无需先升级 PowerShell 版本。这只需要做一些小改动,比如下面这个命令,它在使用 Invoke-WebRequest 之前启用 TLS v1.2,因为它过去默认不启用:
[Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12在另一个项目中,我正在尝试将我旧的 Tumblr 博客合并到我的个人主网站中,该网站使用 Eleventy 静态站点生成器。当时没有现成的工具可以做到这一点,所以我创建了一个 “Tumblr to Eleventy” PowerShell 脚本。
该脚本有两个主要输入:Tumblr 的归档导出(一个包含大量 HTML 和媒体附件的 ZIP 文件)以及 Tumblr API。它使用 Invoke-WebRequest 和 ConvertFrom-Json 来检查 API 并遍历博客上的所有帖子,使用 Expand-Archive 解压 ZIP 文件,并使用 Copy-Item 和 New-Item 等 cmdlet 来创建适合 Eleventy 站点的新文件结构。
最重要的部分是保留原始 Tumblr 帖子的所有元数据,包括标题、永久链接、发布日期和标签。脚本通过创建一个包含所有数据的自定义对象,然后使用 ConvertTo-Json cmdlet 将其转换为 Eleventy 友好的 JSON 格式 来实现这一点:
$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$Post 变量包含来自 Tumblr API 的响应,因此这只是将该数据映射到 Eleventy 期望的键。例如,API 有时会将我的帖子标题放在 summary 而不是 title 中,因此脚本会先检查 .title,并使用 .summary 作为回退。Tumblr 以 Unix 时间 提供每个帖子的发布日期和时间,PowerShell 可以将其转换为 Eleventy 所需的 UTC 格式。

如果不是 PowerShell,我可能会用 Node.js 来做这件事,因为 Node 确实有内置的 Web 请求 和 文件处理 功能。然而,对于解压归档这样的简单任务(要么调用系统的 unzip,要么使用 JS 原生库),它需要外部依赖,而且到处都是 await 语句。我最终没有迁移我的博客,但希望其他人会觉得这个脚本有用。
工具箱中的另一件工具
我还在熟悉 PowerShell 脚本编写,但每完成一个项目,我对它的印象就更深刻。它是跨平台的,比 Bash 脚本更可预测、更可复用,而且相对容易理解。我简直不敢相信这和 Copilot 出自同一家公司。
当然,如果你以酷炫的方式进行脚本编写和自动化,让 Claude 或其他 AI 聊天机器人来掌舵,这些都不重要。机器人可以为你完成思考和测试。然而,我喜欢学习,并且正在尽最大努力防止我的大脑萎缩。在 LM Studio 中运行的本地 Gemma 4 模型确实在网页搜索失败时帮助我进行故障排除,但到目前为止,我的绝大部分脚本都是充满爱意地手工编写的。
如果你有兴趣学习如何使用 PowerShell,有大量的在线指南和视频教程。我一直在看 JackedProgrammer 的“PowerShell 7 for Beginners”,我甚至可能在某天买一本参考书。它可以正好放在我那本 1996 年平装版 O'Reilly 的HTML: The Definitive Guide 旁边。
来源:Hacker News · spacebar.news