PowerShell

PowerShell似乎借鉴了很多Bash的设计, 许多特性在PowerShell里以相同的形式存在.
  • 由于Powershell的命令过多, 且命令的名称普遍冗长, 命令的缩写形式很难记忆,
    这导致在终端里编写Powershell命令组合很容易丧失理智, 经常需要将命令组合编写成脚本文件.
    与之相对的, Bash/Zsh命令 + GNU程序很容易在终端里编写和执行.
  • 社区偏小, 对于Powershell的很多问题, 在StackOverflow里都无法找到好的答案.
    好在New Bing这样的LLM可以替用户快速生成PowerShell脚本.
  • Windows系统自带的IDE Windows PowerShell ISE是个极其愚蠢的编辑器, 应该被禁止使用:
    • 不支持高版本PowerShell的智能感应.
    • 保存文件使用的编码不是UTF-8.
  • 脑残的Cmdlet设计:
    • https://github.com/PowerShell/PowerShell/issues/6232
    • Get-ChildItem 会在只有一个文件的情况下返回 FileInfo 而不是 FileInfo[].
      为了强制返回数组, 需要写成 @(Get-ChildItem ...).
  • 错误处理设计得一塌糊涂: https://github.com/MicrosoftDocs/PowerShell-Docs/issues/1583
  • 不支持在运行命令时临时设置环境变量:
    • https://stackoverflow.com/questions/1420719/powershell-setting-an-environment-variable-for-a-single-command-only
    • https://github.com/PowerShell/PowerShell/issues/3316
  • 几乎所有文件系统相关的命令都有 -LiteralPath-Path 两种路径参数.
    然而默认的路径参数是 -Path, 该参数的通配符语法里的 [] 恰好使用了在路径中合法的方括号,
    这项设计使得所有包含方括号的路径都需要进行转义, 完全是一项制造bug的设计.
PowerShell的 help / man / Get-Help, 用于查看命令相关的手册内容.
PowerShell从v3开始, 可以通过 Update-Help 来在线更新手册内容.
help支持通配符, 可以用 help *log* 查找所有名字里包含log的命令.
查找命令示例 help Get-Command -example.
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/set-strictmode
默认情况下, PowerShell容忍很多错误, 比如变量可以在被初始化之前使用.
严格模式有多个版本, 版本号越大限制越严格.
建议直接指定为Latest, 尽管这会因为PowerShell版本的变化而具有不同的限制, 但总比无意中使用反模式要好.
Set-StrictMode -Version Latest
直接访问脚本或函数的参数数组.
这在编写用于简写程序调用的PowerShell脚本时很有用.
# 设置环境变量
$Env:VAR = 'Value'
# 删除环境变量
Remove-Item Env:\VAR
Get-Alias 命令可以查找到所有别名.
PowerShell没有像Zsh那样内置基于glob的路径扩展功能, 但可以手动通过 Resolve-Path 命令达到类似效果.
'*.wav' | Resolve-Path
# or
(Resolve-Path '*.wav')
PowerShell的Windows版本内置一个 Show-Command 命令, 该命令可以弹出一个用户界面, 可用于填写命令的参数.
# 这种方法的缺陷是不能在转义符号之后编写注释.
echo `
foo `
bar
# 以下代码无法以预期方式执行(值得一提的是, Bash也不支持这样写注释):
echo ` # 注释
foo ` # 注释
bar
# 幸运的是, 由于PowerShell支持注释块, 因此可以通过注释块来处理这种情况:
echo <# 注释 #> `
foo <# 注释 #> `
bar
PowerShell的主要特点是它的命令输出是对象而不是文本.
列出对象的所有成员的名称, 成员类型, 类型定义.
这个命令最常见的用法是接在管道后面, 例如 Get-Process | gm.
选择对象的属性列, 例如 Get-Process | select name, id 可以只列出进程名和id.
该命令会弹出一个网格UI, 用户可以在UI里执行过滤, 排序等数据查看操作.
$colorPicker = @('blue', 'white', 'yellow', 'black')
# 等同于
$colorPicker = 'blue', 'white', 'yellow', 'black'
# 访问数组成员
$colorPicker[0] # 'blue'
$colorPicker[1..3] # 'white', 'yellow', 'black'
# 尾部追加
# PowerShell里的数组是定长的, 每次向数组添加新的成员时, 都是在重新创建数组.
$colorPicker = $colorPicker + 'orange'
# 等同于
$colorPicker += 'orange'
# 连结两个数组
$colorPicker = $colorPicker + @('pink', 'cyan')
# 获得数组长度
$colorPicker.Length
通过借用.Net的数据结构, 可以在PowerShell里使用变长数组.
$colorPicker = [System.Collections.ArrayList] @('blue', 'white', 'yellow', 'black')
# 尾部追加
$colorPicker.Add('gray')
$users = @{
foo = 'bar'
baz = 'qux'
}
$users['foo'] # 'bar'
$users.foo # 'bar'
$users.Keys # 'foo', 'bar'
$users.Values # 'bar, 'qux'
$users.Add('key', 'value')
$users['key'] = 'value'
$users.ContainsKey('foo')
$users.Remove('foo')
括号内的命令会被优先执行, 然后其输出会被填到相应的位置上:
echo (cat text.txt)
$true, $false.
PowerShell还会隐式将任何类型视为布尔值.
PowerShell里的单引号字符串不会插入变量, 双引号字符串会插入变量, 跟Bash表现一致.
$null
上一个命令的退出状态码.
if (...) {
# ...
} elseif (...) {
# ...
} else {
# ...
}
switch (...) {
expression1 {
# ...
break
}
expression2 {
# ...
break
}
default {
# ...
}
}
foreach ($element in $collection) {
# ...
}
for ($i = 0; $i -lt 10; $i++) {
# ...
}
$counter = 0
while ($counter -lt 10) {
# ...
$counter++
}
do {
# ...
} while (...)
do {
# ...
} until (...)
# 最佳实践: 让PowerShell在出现错误后将其升级为终止错误, 从而导致脚本停止运行.
$ErrorActionPreference = 'Stop' # 该变量的默认值为'Continue'.
# 可以通过给命令设置`-ErrorAction`参数来临时改变一条命令出现错误时采取的行为.
Get-ChildItem -Path $folderPath -ErrorAction Continue
try catch 只能捕获终止错误 (terminating error).
try {
# ...
} catch { # 类似C#, catch后可跟错误类型, 可以使用多个catch块
# 获得异常
$_.Exception
# ...
} finally {
# ...
}
外部程序的非零退出不被PowerShell认为是终止错误(这是非常反直觉的), 因此无法用try catch来捕获调用的外部程序的非零退出.
./run.exe
if ($LASTEXITCODE -ne 0) {
# ...
}
一个数组类型的自动变量, 当前会话里的所有错误都会被保存进去.
索引越小的错误越新.
PowerShell里提供的类似于lambda表达式的代码块, 常用于 Where-Object 这样的命令.
例子:
Get-Serivce | Where-Object {$_.Status -eq 'Running'}
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions
# 参数写法1 (官方手册推荐这种写法, 但实在很丑)
function <name> {
param (
[type]$parameter1
[, [type]$parameter2]
)
<statement list>
}
# 参数写法2
function <name> [([type]$parameter1[, [type]$parameter2])] {
<statement list>
}
从控制台获得用户输入.
在控制台输出内容.
在控制台输出内容.
这个命令与 Write-Output 的区别在于, 该命令会绕过管道系统, 因此无法被其他命令获取到输出内容.
调用命令.
访问类的静态成员.
范围运算符, 1..3 相当于 1, 2, 3
类型检查.
类型转换.
文本格式化运算符.
即函数式编程中的map.
即函数式编程中的filter.
指代管道中当前的对象.