PowerShell 快速上手(针对有 C# 经验者)

PowerShell 是微软一个非常棒的产品,终于改变了人们对 Windows 下命令行的一片鄙视。一直以来都很想学学 PowerShell,这样我就不用使用 Python 作为手边的脚本语言了,对进行一些文件操作、系统操作而言 PowerShell 还是要比 Python 来的方便。另外由于有不少 C# 的经验,因此 PowerShell 能够调用.NET 库的特性也能够充分利用起来,并且实际发现 C# 的经验让我学习 PowerShell 快了不少(当然使用 bash、CMake 这些脚本的经验应该也帮上忙了吧)。本文就是一篇针对有 C# 经验的用户的 PowerShell 上手教程,当然如果你有 F#、IronPython 等基于.NET 的脚本经验那就更好了。

Note: 后文中 PowerShell 简称 PS。

PowerShell 脚本的运行方法

PowerShell 本质是一个脚本环境 / 脚本语言。在 PS 中,语句从第一行开始依次执行,而不像普通程序一样会有一个显式的程序入口(Entry),并且 PS 的每一行命令(语句)都不用使用结尾标志(如 C 家族的;)。因此 PS 脚本的编写将会非常直观,想要输出 Hello World 只需一行语句即可:

1
echo "Hello World"

运行一段 PS 脚本有两种方式,一种是在 PS 交互命令行中直接将命令输入,例如你可以直接在 PS 命令行中输入 35+2,或者 $env:Path.GetType() 等复杂语句;另一种则是将脚本写在.ps1 文件中,然后运行.ps1 文件即可。这与大部分脚本语言运行的方法是一致的。

需要指出的是 Windows 下 PS 脚本运行是受到系统的限制的,默认系统配置下,如果再 PowerShell 窗口中直接运行.ps1 脚本,PS 会提示 File cannot be loaded because running scripts is disabled on this system.。解决这个问题有两种方式

  1. 将系统中 Windows PowerShell 执行策略改为 “Bypass”,这个可以通过在 PS 中运行以下命令完成
    1
    2
    Set-ExecutionPolicy Bypass -Scope CurrentUser
    Set-ExecutionPolicy Bypass -Scope Process # 仅在当前PS进程中生效。
  2. 使用 powershell -noprofile -executionpolicy bypass -file <文件路径> [参数] 来运行脚本,这是用户友好的方法。由于该命令可以通过 cmd 或者 PS 执行,因此可以通过.bat 文件封装这个命令,以达到双击运行的效果。

命令 or 参数 or 字符串?

PowerShell 中变量名均以 $ 开头。与 bash、CMake 风格相似的是,PS 中的内容如果不以 $ 或者 - 开头则默认都是命令或者字符串,例如在 echo Hello World 这样的语句中,echo 是命令,HelloWorld 都是命令的参数并且都是字符串类型。在 PS 中,命令 / 函数的调用有两种形式:

  1. 命令形式:<命令> [参数1] .. [-参数名2 <参数2>] ..,如 Get-ChildItem C:\Windows\* -Include *.exe。在这类形式中,参数默认都是字符串,在不含空格时都可以不打引号。这里的命令主要是 PS 内置的命令(cmdlet)或者别名(alias),通常是一个首字母大写的单词或者用短横连接的两个首字母大写单词。
  2. 函数形式: [对象.]函数(参数1, 参数2, ..),如 "abcd".Endswith("cd")。在这类形式中,参数需要通过引号来明确指定是字符串。通过这样的方法使用的是.NET 的函数,写起来就和 C# 没什么区别。

另外,PowerShell 支持管道,因此对于命令形式来说可以通过 <命令1> | <命令2> | .. 的形式来进行链式调用并且传递参数,而函数形式就没法进行链式调用了。

.NET 交互(Interop)

PowerShell 本身是用 C# 编写的,并且其内部运行环境也是基于.NET 的,因此 PS 与.NET 交互十分简单。

PowerShell 对象

PS 中的对象均为.NET 中的对象,因此 PS 变量可以直接当作.NET 对象进行操作,例如下列语句:

1
2
3
4
5
6
7
>> 123.ToString()           # 报错,因为123既不是变量也不是命令
>> ([int]123).ToString() # 这里将字符串123转换成了System.Int32变量
123
>> (Dir C:\).GetType().Name
Object[]
>> (Get-Item HKCU:\Software).GetType().FullName
Microsoft.Win32.RegistryKey

PS 不仅能操作对象,还能够直接创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
# PS中创建.NET对象
>> New-Object -TypeName System.Version -ArgumentList "1.2.3.4"
Major Minor Build Revision
----- ----- ----- --------
1 2 3 4

# PowerShell中的Object对象操作
>> $var = New-Object object
>> $var.GetType().Name
System.Object
>> Add-Member -InputObject $var -Name Value -Value 123 -MemberType NoteProperty
>> $var.Value
123

通过 Add-MemberGet-Member 命令,PS 不仅能够完成.NET 中的反射(Reflection),还能够完成类似匿名对象(Anonymous Object)的创建,这个十分有用!

PowerShell 中操作.NET 类

在 PS 中可以直接调用.NET 库中的类进行操作

  1. 新建对象:New-Object -TypeName System.Version -ArgumentList "1.2.3.4"
  2. 强制转换:[int]"1234"(等同于 [System.Int32]"1234"
  3. 调用静态函数: [System.DateTime]::Parse("2012-10-13 23:42:55")
    有了这些操作,PowerShell 已经完全可以当一个 C# 脚本引擎了~

操作符

在利用 PS 写逻辑流程时就会发现,除了加减乘除外,C# 的很多关键字和操作符都不能直接使用。在 PS 中,这些操作符都变成了命令或者以 - 开头的命令符了。下表列出了常用的操作符:

类型PS 操作符C# 操作符
比较-eq==
比较-ne!=
比较-gt>
比较-ge>=
比较-lt<
比较-le<=
类型-isis
类型-asas
逻辑-and&&
逻辑-or||
逻辑-xor^
逻辑-not!
运算-band&
运算-bnot~
运算-bor|
运算-bxor^
运算-shl<<
运算-shr>>
运算-bxor^

另外需要注意的是 >>> 等等运算符在 PS 中是重定向运算符。

尽管名字不一样,但是这些操作符的用法还是一样的。另外 PS 中还定义了一些.NET 中没有的操作符,这些操作符通常很实用,如:

  • -contains:返回集合包含元素
  • -in:返回元素是否被集合包含
  • -like: 返回字符串是否与通配式匹配(Wildcard Pattern
  • -match: 返回字符串是否与正则表达式匹配(Regex
  • -f: 等价于 String.Format()

PowerShell “驱动”

“驱动”(Drive)是 PowerShell 中很有意思的一个概念,PowerShell 提供了一种统一的接口来管理层次信息系统,包括文件系统(FileSystem)、注册表(Registry)、证书(Certificate)、环境变量(Environment),甚至包括当前会话的变量(Variable)和函数(Function)。这些 “Drive” 支持统一的一套接口,这就非常的有趣了 233。

通过 Get-PSDrive 命令我们可以获得当前 PowerShell 会话支持的驱动,在我的电脑上该命令的运行结果如下(省去部分内容)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Name         Provider      Root 
---- -------- ----
Alias Alias
C FileSystem C:\
Cert Certificate \
D FileSystem D:\
E FileSystem E:\
Env Environment
Function Function
G FileSystem G:\
HKCU Registry HKEY_CURRENT_USER
HKLM Registry HKEY_LOCAL_MACHINE
Variable Variable
WSMan WSMan

通过这些驱动,ls env:\Path 就能够获取系统环境变量,HKCU:\SOFTWARE 就可以获取注册表键,十分方便。但是需要注意的是这些驱动获得的对象的类型是不同的,如文件对象会是 System.IO.FileInfo,而注册表键则是 Microsoft.Win32.RegistryKey

驱动统一支持的命令接口

CmdletAlias用途
Get-ChildItemdir, ls获取当前位置的所有子项
Set-Locationcd更改当前位置
New-Itemmd, mkdir在指定位置新建对象
Get-Item获取该位置对应的对象
Remove-Itemdel, rm删除指定位置的对象
New-ItemProperty新建对象的属性
Get-ItemProperty获取对象的属性(如文件属性,注册表键)
Set-ItemProperty设置对象的属性
Clear-ItemProperty删除对象的属性
Test-Path判断指定位置是否存在对象

看完这些内容以后你就可以像写C#一样开始写PowerShell啦。更加详细的教程可以参见下方的参考资料~

参考资料