Windows PowerShell 学习笔记其二(变量与控制语句)

重定向与管道

重定向

可以借助管道符Out-File 命令将某个命令的输出内容重定向至文本文件中。

如:Get-ChildItem | Out-File files.txt
通过 Get-ChildItem(即 dir)获取当前目录下的文件列表,再借助管道和 Out-File 将列表保存在 files.txt 文件中。

在使用 Out-File 命令时可以带上 -Encoding 等选项来指定输出文件的编码等属性:
Get-Content filename.cs | Out-File -Encoding ASCII file.txt
Get-ChildItem | Out-File -Width 120 files.cs

或者也可以使用类似 bash 里的 > 符号:
Get-ChildItem > files.txt
Get-ChildItem 2> errors.txt
Get-ChildItem n> otherStreams.txt

在文件末尾添加内容

在使用 Out-File 命令的同时,可以附加上 -Append 选项用来指明在文件末尾添加内容,如:
Get-ChildItem | Out-File -Append files.txt

同样也可以使用类似于 bash 中的 >> 符号:
Get-ChildItem >> files.txt

管道

简单来说,管道可以用来连接多个命令,使得上一个命令的输出作为下一个命令的输入,从而将多个命令以“首尾相接”的方式执行。

Get-Process | Where-Object WorkingSet -gt 100mb | Sort-Object -Descending WS
获取系统当前的进程信息,并筛选出内存占用大于 100MB 的进程,再将筛选结果按照占用的内存由大到小排序后输出。

1
2
3
4
5
6
7
8
9
10
PS C:\Users\starky> Get-Process | Where-Object WorkingSet -gt 100mb | Sort-Object -Descending WS

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
997 75 342180 249576 1,265.59 8516 0 MsMpEng
1598 103 147168 203348 40.02 5992 3 SearchUI
806 29 167004 173960 186.02 5288 3 chrome
1757 182 75120 153712 195.45 13116 3 chrome
2816 134 89428 124596 126.25 1480 3 explorer
367 38 83872 116840 74.64 416 3 chrome

筛选(Where-Object)

Where-Object 命令可以对某个列表内容或命令的输出应用各种类型的筛选条件。它的默认别名为 where?

Get-Process | Where-Object { $_.Name -like "Baidu*" }
获取当前系统中名字以“Baidu”开头的进程及其信息

上面的命令同时也可以这样表述:
Get-Process | Where-Object Name -like "Baidu*"

即先通过 Get-Process 命令获取全部进程信息,再将它们传递给 Where-Object 命令。而 Where-Object 又指定每一个进程的 Name 属性(即进程名称)与模式 Baidu* 进行匹配,最后只输出匹配的结果。

1
2
3
4
5
6
PS C:\Users\starky> gps | where { $_.Name -like "Baidu*" }

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1030 251 58288 98076 552.59 204 3 BaiduNetdisk
187 14 11952 8628 0.06 4856 3 BaiduNetdiskHost

其他示例如筛选未响应的进程:
Get-Process | where { -not $_.Responding }

筛选已经停止的服务:
Get-Process | where { $_.Status -eq "Stopped" }

遍历(Foreach-Object)

Foreach-Object 命令(默认别名为 foreach%)用于对某个列表中的每一个对象指定特定的操作。如:

1
2
3
4
5
6
PS C:\Users\starky> 1..5 | Foreach-Object { $_ * 2 }
2
4
6
8
10

又比如筛选当前目录下所有的文本文件,并去掉它们的只读属性:
Get-ChildItem *.txt | Foreach-Object { attrib -r $_ }

其中 $_ 表示传递给 Foreach-Object 的每一个对象。attrib -r $_ 即表示对 Get-ChildItem *.txt 获取到的所有文本文件去除只读属性。

又如:

1
2
3
4
5
PS C:\Users\starky> $myArray = 1,2,3,4,5
PS C:\Users\starky> $sum = 0
PS C:\Users\starky> $myArray | Foreach-Object { $sum += $_ }
PS C:\Users\starky> $sum
15

上面的命令也可以使用如下形式:

1
2
3
PS C:\Users\starky> $myArray = 1,2,3,4,5
PS C:\Users\starky> $myArray | Foreach-Object { $sum = 0 } { $sum += $_ } { $sum }
15

格式化输出

PowerShell 中的许多命令默认情况下是以“表格”的形式来格式化输出的,如:

1
2
3
4
5
6
PS C:\Users\starky> Get-Process PowerShell

Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
410 41 60444 75004 550 3.21 4212 powershell
428 43 60688 56684 561 4.04 5288 powershell

实际上在多数情况下,命令的“真实”输出包含了更加丰富的信息,可以使用 Format-List 命令比较一下效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
PS C:\Users\starky> Get-Process PowerShell | Format-List *


__NounName : Process
Name : powershell
Handles : 404
VM : 575397888
WS : 76754944
PM : 61808640
NPM : 41736
Path : C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe
Company : Microsoft Corporation
CPU : 3.2136206
FileVersion : 6.3.9600.16406 (winblue_gdr_oob.130926-1103)
ProductVersion : 6.3.9600.16406
Description : Windows PowerShell
Product : Microsoft® Windows® Operating System
Id : 4212
PriorityClass : Normal
HandleCount : 404
WorkingSet : 76754944
PagedMemorySize : 61808640
PrivateMemorySize : 61808640
VirtualMemorySize : 575397888
TotalProcessorTime : 00:00:03.2136206
BasePriority : 8
...

Format-List 是 4 种格式化输出的命令之一,其他还有 Format-TableFormat-WideFormat-CustomFormat-List 用来接收输入内容并将其以列表的形式输出。

默认情况下,不带任何参数的格式化命令只会输出对象的一小部分属性,如:

1
2
3
4
5
6
7
8
9
10
11
PS C:\Users\starky> Get-Process PowerShell | Format-List

Id : 4212
Handles : 428
CPU : 3.4632222
Name : powershell

Id : 5288
Handles : 392
CPU : 4.1028263
Name : powershell

Format-List * 则会显示输入对象的所有属性。

同时,也可以在格式化命令后面手动指定需要显示的属性或参数,如:

1
2
3
4
5
6
PS C:\Users\starky> Get-Process PowerShell | Format-Table Id,Name,CPU,WS -Auto

Id Name CPU WS
-- ---- --- --
4212 powershell 3.7128238 78704640
5288 powershell 4.1028263 58068992

变量与对象

变量

在 PowerShell 中,可以将命令的输出或其他内容先保存在某个变量(以 $ 符号为前缀)中,以供后续使用(甚至可以把变量直接传递给管道符)。

1
2
3
4
5
6
7
8
9
10
11
PS C:\Users\starky> $result = 2 + 2
PS C:\Users\starky> $result
4
PS C:\Users\starky> $processes = Get-Process
PS C:\Users\starky> $processes.Count
64
PS C:\Users\starky> $processes | Where-Object { $_.ID -eq 0 }

Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
0 0 0 24 0 0 Idle
访问环境变量

PowerShell 可以很轻松地访问系统中定义的环境变量,如使用 Get-ChildItem env: 命令获取当前系统已定义的所有环境变量的列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PS C:\Users\starky> Get-ChildItem env:

Name Value
---- -----
ALLUSERSPROFILE C:\ProgramData
APPDATA C:\Users\starky\AppData\Roaming
CommonProgramFiles C:\Program Files\Common Files
CommonProgramFiles(x86) C:\Program Files (x86)\Common Files
CommonProgramW6432 C:\Program Files\Common Files
COMPUTERNAME Starky-Lenovo
ComSpec C:\windows\system32\cmd.exe
FP_NO_HOST_CHECK NO
HOMEDRIVE C:
HOMEPATH \Users\starky
LOCALAPPDATA C:\Users\starky\AppData\Local
...

也可以使用 $env:variablename 形式的变量名直接表示系统环境变量。几种形式的示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PS C:\Users\starky> Get-ChildItem env:username

Name Value
---- -----
USERNAME starky


PS C:\Users\starky> Get-ChildItem Environment::username

Name Value
---- -----
USERNAME starky


PS C:\Users\H19038> $env:username
starky

作用域

创建一个只在特定范围内生效的变量,使用如下形式的语法:
$SCOPE:variable = value

获取特定的作用域里某个变量的值,使用如下形式的语法:
$SCOPE:variable

如创建一个在脚本执行完毕后仍旧有效的变量,使用 GLOBAL 作用域:
$GLOBAL:variable = value

在某个函数内部更改脚本范围内的变量,需要显式地指定 SCRIPT 作用域:
$SCRIPT:variable = value

PowerShell 中变量的作用域,就是控制各变量在不同范围内的可见性。比如当进入一个代码块、函数或别名时,当前的作用域成为新的“本地作用域”(子作用域),原来的作用域则成为“父作用域”。
子作用域可以访问父作用域中定义的所有变量,但是没有权限直接修改这些变量的值。
换句话说,子作用域可以修改在父作用域中定义的变量,但是这种修改不会将新值自动同步到父作用域。

.NET 对象

PowerShell 可以直接访问 .NET 对象的方法(包括静态方法和实例)和属性,比如访问某个静态方法:
[ClassName]::MethodName(parameter list)

访问某个对象实例绑定的方法:
$objectReference.MethodName(parameter list)

访问某个类的静态属性:
[ClassName]::PropertyName

访问某个对象实例的属性:
$objectReference.PropertyName

下面是一些具体的例子。

静态方法

1
2
3
4
5
PS C:\Users\starky> [System.Diagnostics.Process]::GetProcessById(0)

Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
0 0 0 24 0 0 Idle

实例方法

1
2
3
PS C:\Users\starky> $now = Get-Date
PS C:\Users\starky> $now.ToString()
2019/2/27 15:34:21

静态属性

1
2
3
PS C:\Users\starky> [System.DateTime]::Now

2019年2月27日 15:36:09

实例属性

1
2
3
PS C:\Users\starky> $today = Get-Date
PS C:\Users\starky> $today.DayOfWeek
Wednesday

创建对象的实例

使用 New-Object 命令可以创建某个 .NET 对象的实例。如:

1
2
3
PS C:\Users\starky> $generator = New-Object System.Random
PS C:\Users\starky> $generator.NextDouble()
0.697396862179691

也可以在创建实例的同时直接使用它:

1
2
3
4
5
PS C:\Users\starky> (New-Object Net.WebClient).DownloadString("http://www.baidu.com")
<!DOCTYPE html><!--STATUS OK-->
<html>
<head>
...

通常的做法是,创建对象实例的同时,还要为其指定某些属性。如:

1
2
3
4
5
6
7
8
9
10
11
PS C:\Users\starky> $startInfo = New-Object Diagnostics.ProcessStartInfo -Property @{
>> 'Filename' = "powershell.exe";
>> 'WorkingDirectory' = $HOMEPATH;
>> 'Verb' = "RunAs"
>> }
>>
PS C:\Users\starky> [Diagnostics.Process]::Start($startInfo)

Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
4 2 260 1224 6 0.00 6248 powershell

上述命令中创建 Diagnostics.ProcessStartInfo 对象的语句也可以简写为如下形式:

1
2
3
4
5
$startInfo = [Diagnostics.ProcessStartInfo] @{
'Filename' = "powershell.exe";
'WorkingDirectory' = $HOMEPATH;
'Verb' = "RunAs"
}

有时候为了简写 .NET 类的完整名称,可以借助变量赋值,如:

1
2
3
4
5
PS C:\Users\starky> $math = [System.Math]
PS C:\Users\starky> $math::Min(1,10)
1
PS C:\Users\starky> $math::Sin(3.14)
0.00159265291648683

循环与流程控制

比较与逻辑运算

PowerShell 支持的比较运算符
-eq, -ne, -gt, -in, -notin, -lt, -le, -like, -notlike, -match, -notmatch, -contains, -notcontains, -is, -isnot

逻辑运算符
-and, -or, -xor, -not

逻辑与比较运算符可以用来在数据间进行比较,同时也可以测试当前某些特定的条件是否成立。
如判断当前目录下的文件个数是否大于等于 4:

1
2
PS C:\Users\starky> (dir).Count -ge 4
True

某个字符串是否匹配特定的正则表达式:

1
2
PS C:\Users\starky> "Hello World" -match "H.*World"
True

默认情况下,PowerShell 里的比较运算是区分大小写的,如果想不区分大小写,可以使用如下版本的比较运算符:
-ceq, -cne, -cge, -cgt, -cin, -clt, -cle, -clike, -cnotlike, -cmatch, -cnotmatch, -ccontains, -cnotcontains

逻辑运算符可以组合多个值为 truefalse 的语句,并根据运算符号的不同返回特定的逻辑运算结果。
比如判断某个字符串是否匹配特定模式,且字符串长度大于 10:

1
2
3
PS C:\Users\starky> $data = "Hello World"
PS C:\Users\starky> ($data -like "*llo W*") -and ($data.Length -gt 10)
True

条件语句

PowerShell 中的 if 语句基本用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$temperature = 35

if($temperature -le 0)
{
"Freezing"
}
elseif($temperature -le 10)
{
"Cold"
}
elseif($temperature -le 20)
{
"Warm"
}
else
{
"Hot"
}

除了流程控制,条件语句也经常用来对变量进行赋值,形式如下:

1
2
3
4
5
6
PS C:\Users\starky> $result = if(Get-Process -n notepad) { "Running" } else { "Not running" }
Get-Process : 找不到名为“notepad”的进程。请验证该进程名称,然后再次调用 cmdlet。
...

PS C:\Users\starky> $result
Not running

通常情况下,使用 switch 语句可以替代包含大量 if ... elseif ... else 的语句。
PowerShell 的 switch 在对用户输入进行条件判断时,支持多种选项的使用,如通配符正则表达式甚至简短的代码块,相比于 C 和 C++ 中的 switch 语句更显得强大。

1
2
3
4
5
6
7
8
9
10
$temperature = 35

switch($temperature)
{
{ $_ -lt 0 } { "Below Freezing"; break }
32 { "Exactly Freezing"; break }
{ $_ -le 10 } { "Cold"; break }
{ $_ -le 20 } { "Warm"; break }
default { "Hot" }
}
循环

for 循环

1
2
3
4
for($counter = 1; $counter -le 10; $counter++)
{
"Loop number $counter"
}

foreach 循环

1
2
3
4
foreach($file in dir)
{
"File name: " + $file.Name
}

或者:dir | foreach { "File name: " + $_.Name }

while 循环

1
2
3
4
5
$response = ""
while($response -ne "QUIT")
{
$response = Read-Host "Type something"
}

do..while 循环

1
2
3
4
5
$response = ""
do
{
$response = Read-Host "Type something"
} while($response -ne "QUIT")

do..until 循环:

1
2
3
4
5
$response = ""
do
{
$response = Read-Host "Type something"
} until($response -eq "QUIT")

参考书籍

Windows PowerShell Cookbook, 3rd Edition