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 的进程,再将筛选结果按照占用的内存由大到小排序后输出。
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*
进行匹配,最后只输出匹配的结果。
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
和 %
)用于对某个列表中的每一个对象指定特定的操作。如:
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
获取到的所有文本文件去除只读属性。
又如:
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
上面的命令也可以使用如下形式:
PS C:\Users\starky> $myArray = 1,2,3,4,5
PS C:\Users\starky> $myArray | Foreach-Object { $sum = 0 } { $sum += $_ } { $sum }
15
格式化输出
PowerShell 中的许多命令默认情况下是以“表格”的形式来格式化输出的,如:
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
命令比较一下效果:
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-Table
、Format-Wide
、Format-Custom
。Format-List
用来接收输入内容并将其以列表的形式输出。
默认情况下,不带任何参数的格式化命令只会输出对象的一小部分属性,如:
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 *
则会显示输入对象的所有属性。
同时,也可以在格式化命令后面手动指定需要显示的属性或参数,如:
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 中,可以将命令的输出或其他内容先保存在某个变量(以 $
符号为前缀)中,以供后续使用(甚至可以把变量直接传递给管道符)。
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:
命令获取当前系统已定义的所有环境变量的列表:
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
形式的变量名直接表示系统环境变量。几种形式的示例如下:
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
下面是一些具体的例子。
静态方法:
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
实例方法:
PS C:\Users\starky> $now = Get-Date
PS C:\Users\starky> $now.ToString()
2019/2/27 15:34:21
静态属性:
PS C:\Users\starky> [System.DateTime]::Now
2019年2月27日 15:36:09
实例属性:
PS C:\Users\starky> $today = Get-Date
PS C:\Users\starky> $today.DayOfWeek
Wednesday
创建对象的实例
使用 New-Object 命令可以创建某个 .NET 对象的实例。如:
PS C:\Users\starky> $generator = New-Object System.Random
PS C:\Users\starky> $generator.NextDouble()
0.697396862179691
也可以在创建实例的同时直接使用它:
PS C:\Users\starky> (New-Object Net.WebClient).DownloadString("http://www.baidu.com")
<!DOCTYPE html><!--STATUS OK-->
<html>
<head>
...
通常的做法是,创建对象实例的同时,还要为其指定某些属性。如:
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
对象的语句也可以简写为如下形式:
$startInfo = [Diagnostics.ProcessStartInfo] @{
'Filename' = "powershell.exe";
'WorkingDirectory' = $HOMEPATH;
'Verb' = "RunAs"
}
有时候为了简写 .NET 类的完整名称,可以借助变量赋值,如:
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:
PS C:\Users\starky> (dir).Count -ge 4
True
某个字符串是否匹配特定的正则表达式:
PS C:\Users\starky> "Hello World" -match "H.*World"
True
默认情况下,PowerShell 里的比较运算是区分大小写的,如果想不区分大小写,可以使用如下版本的比较运算符:
-ceq, -cne, -cge, -cgt, -cin, -clt, -cle, -clike, -cnotlike, -cmatch, -cnotmatch, -ccontains, -cnotcontains
逻辑运算符可以组合多个值为 true
或 false
的语句,并根据运算符号的不同返回特定的逻辑运算结果。
比如判断某个字符串是否匹配特定模式,且字符串长度大于 10:
PS C:\Users\starky> $data = "Hello World"
PS C:\Users\starky> ($data -like "*llo W*") -and ($data.Length -gt 10)
True
条件语句
PowerShell 中的 if
语句基本用法如下:
$temperature = 35
if($temperature -le 0)
{
"Freezing"
}
elseif($temperature -le 10)
{
"Cold"
}
elseif($temperature -le 20)
{
"Warm"
}
else
{
"Hot"
}
除了流程控制,条件语句也经常用来对变量进行赋值,形式如下:
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 语句更显得强大。
$temperature = 35
switch($temperature)
{
{ $_ -lt 0 } { "Below Freezing"; break }
32 { "Exactly Freezing"; break }
{ $_ -le 10 } { "Cold"; break }
{ $_ -le 20 } { "Warm"; break }
default { "Hot" }
}
循环
for 循环:
for($counter = 1; $counter -le 10; $counter++)
{
"Loop number $counter"
}
foreach 循环:
foreach($file in dir)
{
"File name: " + $file.Name
}
或者:dir | foreach { "File name: " + $_.Name }
while 循环:
$response = ""
while($response -ne "QUIT")
{
$response = Read-Host "Type something"
}
do..while 循环:
$response = ""
do
{
$response = Read-Host "Type something"
} while($response -ne "QUIT")
do..until 循环:
$response = ""
do
{
$response = Read-Host "Type something"
} until($response -eq "QUIT")