Bootstrap

PowerShell的脚本和命令讲解

目录

一、基础命令... 2

1.1、创建文件... 2

1.2、定制提示... 2

1.3、使用管理员权限... 2

1.4、设置执行策略... 2

1.5、当前目录的所有子目录查找文件名... 2

二、理解cmdlet语法... 3

2.1、向一个参数传递多个值... 3

2.2、使用-WhatIf 3

2.3、使用-Confirm.. 3

三、命令概念... 4

3.1、实现管道... 4

3.2、使用Select-Object选择管道中的对象子集... 4

3.3、使用文件输入和输出操作... 5

3.4、将对象转换为不同的格式... 5

3.4.1、使用ConvertTo-Csv. 5

3.4.2、用Export-Clixml加密导出的凭证对象... 6

四、处理管道数据... 7

4.1、使用比较操作符... 8

4.2、使用通配符和-like操作符... 8

4.3、探索公共数据类型... 8

4.4、使用-is确定数据类型... 9

4.5、使用-match查找字符串的部分... 9

4.6、使用容器操作符-contains和-notcontains. 9

4.7、使用-in和-notin操作符... 10

4.8、使用-replace操作符... 10

五、使用变量... 10

5.1、清理和删除变量... 10

5.2、使用环境变量... 11

六、使用函数... 11

6.1、函数的执行... 11

6.2、Splatting. 11

6.3、创建函数... 12

6.4、使用参数... 13

6.4.1、命名参数... 13

6.4.2、必选参数... 14

6.4.3、位置参数... 15

6.4.4、开关参数... 15

6.5、将管道对象发送给带有Begin、Process和End的函数... 16

七、使用循环... 17

7.1、使用For循环... 17

7.2、使用Foreach循环... 17

7.3、使用If语句... 19

7.4、使用Switch语句... 20

7.5、使用While循环... 22

7.6、使用Where-Object方法... 23

一、基础命令

1.1、创建文件

$Home:存储当前用户的主目录位置。

$PsHome:指向PowerShell的安装目录。

要在不覆盖现有路径文件的情况下创建文件:

$file="C:\Users\XiaZhen\Desktop\123.txt"

if (!(Test-Path $file)) {New-Item -type file -Path $file -Force}

if语句查看变量$file路径是否有现在的路径文件,如果没有,它就在变量$file的路径下创建一个新的文件。

1.2、定制提示

指出当前计算机的名称和当前路径:

function Prompt {$env:COMPUTERNAME + "\" + (Get-Location) + "> "}

1.3、使用管理员权限

使用Run As Administrator打开程序,例如,打开CMD:

Start-Process -FilePath "C:\Path\To\Your\Program.exe" -Verb RunAs

Start-Process cmd -Verb RunAs

1.4、设置执行策略

获取当前策略:Get-ExecutionPolicy

设置当前策略:Set-ExecutionPolicy

Restricted——默认的设置, 不允许任何script运行
AllSigned——只能运行经过数字证书签名的script
RemoteSigned——运行本地的script不需要数字签名,但是运行从网络上下载的script就必须要有数字签名
Unrestricted——允许所有的script运行

Set-ExecutionPolicy RemoteSigned

Get-ExecutionPolicy

1.5、当前目录的所有子目录查找文件名

DIR /S 123.txt

Get-ChildItem -Path "123.txt" -Recurse

Recurse参数指示递归地获取对象,它指示命令的主题是指定的目录及其内容。如果是从cmd.exe运行,提供的输出与DIR /S相同;DIR是Get-ChildItem的别名,所以可以输入:DIR -Path "123.txt" -Recurse

二、理解cmdlet语法

2.1、向一个参数传递多个值

在许多实例中,都希望为一个参数提供多个值。

[-ComputerName <String[]>]

包括字符串在内的整个参数都是可选的,因为整个参数都是用方括号括起来的。<String[]>内部有一个小方括号,看到以这种方式显示的两个方括号时,这意味着可使用逗号分隔的列表传递多个值。例:

Get-EventLog Security -ComputerName Server01,Server02,Server03

这告诉Get-EventLog cmdlet从三个不同的服务器中获取安全日志。还要注意,-LogName参数的语法列为[-LogName]<string>,这里缺少小方括号,意味着对于-LogName参数只能有一个值。从功能上讲,这意味着只能获得一个命名的事件日志。如Security或Application,但不能同时获得两个日志。-ComputerName <String[]>表示,可从多台计算机中获得单个日志。

另一种将多个值加载到参数中的方法是,从文件中读取逗号分隔的值列表。例:

Get-EventLog Security -ComputerName (Get-Content c:\computerlist.txt)

这称为圆括号命令。圆括号内有一个命令,可将值提供给不同的参数。Get-Content将读取文件,每次一行,并将每一行作为-ComputerName参数的单独值放置。圆括号命令的作用就是首先执行括号中的的操作,结果将作为传递给参数的值。还可将值放入变量中,然后变量可将值传递给参数。

$computers=Get-Content c:\computerlist.txt

Get-EventLog -LogName Security -ComputerName $computers

2.2、使用-WhatIf

-WhatIf允许查看cmdlet的结果,而cmdlet不会对系统执行任何更改。如果执行cmdlet,会看到生成的输出,有助于避免发生严重的配置错误,使用-WhatIf作为保险,以确保得到预期的输出。

例:Remove-Item C:\Users\XiaZhen\Desktop\computerlist.txt -WhatIf

该文件从未删除,但它显示了如果执行该cmdlet会发生什么。

2.3、使用-Confirm

 这个参数在运行命令之前请求确认,来帮助降低风险:

-Confirm [:{$true | $false}]

例:Remove-Item C:\Users\XiaZhen\Desktop\computerlist.txt -Confirm

如果选择“是”,就执行操作;“暂停”选项会使当前cmdlet暂停,并并启动一个嵌套的PowerShell会话。这个嵌套的会话通过在命令提示符中添加两个额外的符号(>>)来表示。可先运行其他cmdlet和脚本,输入Exit退出,会返回-Confirm提示。

三、命令概念

3.1、实现管道

将一个命令的输出变成下一个命令的输入,从而将命令连接起来,使用竖杠(|)字符。从左到右依次执行,左边命令的输出添加到管道中,并作为输入发送给右边的命令。例:

Get-EventLog Security | Out-File c:\securityEvents.txt

这将获取本地安全事件日志的内容并将其放入管道中,然后将这些数据发送给Out-File命令,该命令接收管道的内容并将其用作输入源。

3.2、使用Select-Object选择管道中的对象子集

  1. 用户可能不想查看管道集合中的所有对象,如果只想查看集合中顶部或或底部的一些对象,使用Select-Object 进入管道集合,允许选择前面或后面的对象(无论有多少对象),它们对应集合中的行,还可以选择要包含和排除的的特定属性。例如,查看消耗系统RAM的前5个进程:

Get-Process | Sort-Object -Property WorkingSet -Descending | Select-Object -Property WorkingSet,ProcessName -First 5

可选择对象的指定属性,指定-First和-Last可显示集合顶部或底部特定数量的对象,如果在选择对象之前没有对集合进行排序,那么它们的顺序是随机的。使用-Property参数时,Select-Object将删除所有未指定的属性。

  1. 如果知道对象所需属性的特定名称,可根据不同属性进行排序,甚至根据不同属性的组合进行排序,默认情况下,字符串属性不区分大小写,按升序排序。对象基于对象类型的默认属性排序。

更改默认值,以满足特定需求,使用Sort-Object cmdlet,别名Sort。

 

Get-Service | Sort-Object -Property Name -Descending

  1. [[-Property] <Object[]>]说明,可指定一个或多个属性。这是一个可选参数,但可通过逗号分隔的列表传递多个参数。如果传递多个属性,它们将按第一个列出的属性排序,如果多个对象的第一个属性相同,那么对象将按第二个列出的属性排序;如果前两个属性有多个结果,就按第三个属性排序,以此类推。
  2. [-Unique]是一个可选参数,它遍历管道,标识管道集合的唯一成员。任何副本都将被丢弃,此参数不区分大小写。

例,如果希望根据状态和名称对服务进行排序,则可使用以下命令:

Get-Service | Sort-Object -Property Status,Name

检查管道,会看到对象及其属性按Status属性排序,之后按Name属性排序。

3.3、使用文件输入和输出操作

将命令或脚本的结果保存到文件中,可使用重定向操作符,或大于号(>),例:

Get-Process | Sort-Object -Property WorkingSet -Descending | Select-Object -Property WorkingSet,ProcessName -First 5 > "c:\Top 5 Processes RAM.txt"

这种快速方法可获取控制台中显示的内容,并将其直接转储到一个文件中。唯一的问题是这只是是盲目的转储信息;如果想要更好的控制,应用使用Out-File命令。

Get-Process | Sort-Object -Property WorkingSet -Descending | Select-Object -Property WorkingSet,ProcessName -First 5 | Out-File "c:\Top 5 Processes RAM.txt" -NoClobber

-NoClobber参数为确保不覆盖现有文件。Out-File为文本文件使用的格式是Unicode。

Export-Csv创建管道中对象的Csv文件。不需要在转换之前格式化输出,将创建的输出写入CSV格式的文件中, CSV格式以逗号分隔列。

3.4、将对象转换为不同的格式

PowerShell使用两个动词进行对象转换,即ConvertTo和Export。

3.4.1、使用ConvertTo-Csv

语法:

ConvertTo-Csv [-InputObject] <psobject> [[-Delimiter] <char>] [-NoTypeInformation] [<CommonParameters>]

参数-InputObject的值是管道的内容,它在语法中引用为<psobject>,这个管道的对象可用旧的$_或新的$PSItem来标识。

-Delimiter参数允许更改标识各种值的字符。如果不希望用逗号分隔值,而希望用分号,则可以使用-Delimiter“;”参数。

ConvertTo-Csv命令获取对象,并将其转换为逗号分隔的值,每个对象转换为字符串,这些字符串将替换管道的内容。将管道的内容转换为CSV格式,并将管道的当前内容替换为得到的CSV字符串,每个对象都有一个字符串。

放在管道中的第一项是类型信息,该信息可能没有在管道中或在输出文本文件中,可使用-NoTypeInformation参数来禁止它。

Get-EventLog System | Select-Object EventId,EntryType -First 3 | ConvertTo-Csv | Out-File "C:\Events.txt"

此命令在文本文件中提供以下输出:

如果想要将这个CSV导入Excel之类的程序中,就需要清除类型信息。

Get-EventLog System | Select-Object EventId,EntryType -First 3 | ConvertTo-Csv -NoTypeInformation | Out-File "C:\Events.txt"

这个个结果更容易操作和导入。CovertTo-Csv将管道对象集合的内容更改为字符串集合,如果管道中的后续对象没有使用第一个对象定义的属性,或者定义的属性没有值,则其值就设置为空。空值用两个逗号表示。

ConvertTo-Html cmdlet将管道中的对象转换为HTML页面或HTML片段。

Get-EventLog System | Select-Object EventId,EntryType -First 3 | ConvertTo-Html | Out-File "C:\Events.html"

可使用-Head、-Body和-Title参数将这些默认条目替换为可选择的自定义值。

3.4.2、用Export-Clixml加密导出的凭证对象

Export-Clixml的一个常见用法是以加密格式导出凭证。这允许在脚本中存储使用的凭证,而不必在脚本主体内或管道中公开明文形式的凭证。

要获得凭证,可使用Get-Credential cmdlet弹出一个对话框,并将用户名和密码放入一个变量中,最简单的形式是获取凭证,并将其存储到一个变量中:

$CredentialVariable=Get-Credential -Credential "ADSRV\Administrator"

这就提示输入用户名和密码,然后创建一个PSCredential对象。由于使用了-Credential参数,因此已经填充了用户名字段,但它仍然是可编辑的,如果省略-Credential参数,所有字段都为空。

还可创建带有自定义消息的对话框,这称为MessagSet,通过以下代码完成:

$CredentialVariable=Get-Credential -Message "We need your credentials to connect to the remote server"

然后将PSCredential对象存储在已识别的变量中。

PSCredential对象的成员只包含两个属性:Password和Username,查看两者的内容:

用户名以明文形式存储在变量中,而密码以安全字符串的形式存储。访问这些值的方法是指定变量,然后附加想要的成员的名称,以句点开头。

可将凭证导出到XML文件中:

$CredentialVariable=Get-Credential "adsrv\administraotr"

$CredentialVariable | Export-Clixml C:\OurCredentialFile  

也可以使用变量替换路径,使其更模块化。

要将凭证导入脚本,需要使用Import-Clixml命令加载带有对象的变量:

$newCredentialVariable= Import-Clixml C:\OurCredentialFile

用户名仍使用明文显示,但密码已加密。

四、处理管道数据

PowerShell管道可容纳具有各种成员的许多对象,需要消除那些不需要的对象,这种有选择的删除对象的过程称为过滤。要删除一个对象,通常会将其与某些条件进行比较。如果符合条件,为$True,为$True的对象允许保留在管道中;如果对象与条件不匹配,该对象为$False,并被删除。

4.1、使用比较操作符

比较操作符有多种形式,可以区分大小写或不区分大小写,允许使用通配符。如果使用标量值(单个值),将返回$True或$False值,这些称为布尔值。

  1. -eq:表示相等。10 -eq 10的结果是真。
  2. -ne:表示不相等。11 -ne 10的结果是真。
  3. -gt:表示大于。11 -gt 10的结果是真。
  4. -lt:表示小于。11 -lt 10的结果是假。
  5. -le:表示小于或等于。10 -le 10的结果是真,2 -le 10的结果也是真。
  6. -ge:表示大于或等于。11 -ge 10的结果是真,200 -ge 10的结果也是真。

所有这些操作符都不区分大小写。“A” -eq “a”的结果是真;如果希望比较操作区分大小写,应该在操作符前面加上c;比如:-eq变成-ceq;在操作符前面加上i,-eq换成-ieq,表示显式不区分大小写。

4.2、使用通配符和-like操作符

通配符只能用于-like或-clike操作符,通配符*表示任意数量的字符,?表示一个字符:

“Wyoming” -like “W*om?ng”

分析如下:在W和o之间有任意数量的字符,m的后面必须有一个字符,字符串后面必须是n和g,计算为Trun。操作符-clike也执行相同的操作,但区分大小写。例如:“Wadfadkf132omXng” -clike “W*om?ng”。

  1. 比较字符串,确定哪个更大:当使用-gt和-lt计算两个字符串时,值是按字母顺序处理的。意思是a比b小;用clt对字符串求值,表示小写字母“小于”大写字母,例如,“a”小于“A”。
  2. 强制类型:在值或变量前面加上带有方括号的类型可以强制执行类型的转换。例如,将用户加载到变量中的所有输入都视为文本:[String] $somethingTheUserInput,这样,如果用户输入12345,它将转换字符串“12345”,而不是数值12345.

4.3、探索公共数据类型

  1. [string]:固定长度的Unicode字符的字符串。
  2. (char):16位Unicode字符。
  3. [byte]:8位无符号字符。
  4. [int]:32位带符号整数。
  5. [long]:64位带符号整数。
  6. [decimal]:128位的十进制值。
  7. [single]:单精度32位浮点数。
  8. [double]:双精度64位浮点数。
  9. [DateTime]:包含日期和时间。注意格式。
  10. [xml]:XML对象。
  11. [array]:值的数组,就像电子表格的行和列。
  12. [hashtable]:哈希表对象。
  13. [void]:丢弃值。

例如:[string] $myStringVar = “123.456”,这给变量$myStringVar加载字符123.456,这是一些字符,不是一个数字。

[string] $MyStringVar = “123.456”

213.45 -gt $MyStringVar

查看第一个值213.45,其数据类型是[single],因为它没有被引号括起来,并且有一个小数点。第二个值该数据类型被转换为[string],它可转换为[single],因为它包含数字和小数点。因为213.45大于123.456,所以结果为真。

将[int]数据类型从小数类型(例如[single]或[string])转换为仅包含数字字符和小数点的值时,将执行Round()操作,使数值四舍五入;值123.1将转换为123,值123.5将转换为124。如果只想从小数点截去小数点右边的部分,则需要执行单独操作,并调用系统的数学函数:

[long] $myVar = 1234.5

[Math] ::Truncate($myVar)

返回的值是1234,它只是把小数点右边的所有数都去掉了。这是一个临时操作,$myVar仍然等于1234.5,Truncate方法的结果仅用于单个调用,变量的值不会实际改变,如果想使其永远改变,就需要直接将值赋给变量:

$myTruncateValueVar = [Math] ::Truncate($myVar)

如果想获得[System.Math]中其他静态方法的列表:

[System.Math] | Get-Member -Static

4.4、使用-is确定数据类型

如果要确定数据类型,使用type操作is:

[string] $myStringVar = “123.456”

$myStringVar -is [String]

结果是True,因为字符放在引号内,可将数据类型永久转换为[int]:

$mysteryVar = “12345.2343”

$mysteryVar = 12345

$mysteryVar -is [string]

$mysteryVar -is [int]

第一个-is比较的结果是False,第二个比较的结果是True。$mysteryVar是一个字符串,但指定12345时,没有引号或小数点,它就变成整数。还可使用-isnot来反转这个类型操作符。

4.5、使用-match查找字符串的部分

有时需要确定某个文本是否包含在字符串或字符串集合中,操作数-match只能搜索字符串,例如:

“January” -match “Jan”,结果为True,这是标量输入,这意味着它是单个值,而不是数组或数据集合的一部分,这个操作还将填充$Matches自动变量,查看$Matches的值:$Matches

如果想看看到底匹配的是什么内容:

“Srv-Den01”,”DenaliRRAS04”,”DC-Hedenar-05”,”Lon-Win16-CA-05” -match “dEn”

结果如下:

Srv-Den01

DenaliRRAS04

DC-Hedenar-05

因为比较的是测试值与多个引用值(一个数组),所以不会得到True或False,而是得到一个包含所有匹配项的列表。这不会填充$Matches自动变量,如果再次运行$Matches来查看变量的值,它仍然是Jan。也可以反向选择,使用-notmatch,这就显示不匹配的引用值。

4.6、使用容器操作符-contains和-notcontains

容器操作符只返回布尔值True或False,当使用-contains和一个值时,必须将引用值、左操作数、右操作数或测试值匹配起来。例:

“Mark” -contains “M”         结果是False,因为不匹配。

“M”,”Mark” -contains “m”    结果是True,因为其中一个引用值与测试值完全匹配。

-contains的优点是只返回布尔值True或False,如果有多个匹配项,则不必执行其他处理。

引用相等意味着引用值的所有属性和特性必须与测试值的属性和特性完全匹配,由于引用相等的奇异性,它们并不完全相同,例:”M”,”Mark” -contains “M”,”Mark”           结果为False,这是值得注意的。

4.7、使用-in和-notin操作符

这些操作符总是返回布尔值True或False,这次测试值在左边,引用值在右边;此外,如果测试值是一个数组,in操作符将使用引用相等,例:

“J” -in “Jan”,”January”,”J”     结果为True,因为测试值“J”匹配至少一个引用值。

“J” -in “Jan”,”January”         结果为False,因为测试值“J”并不完全匹配任何引用值。

如果切换引用和测试值,就会执行引用相等操作,因为现在测试值是一个数组。例:

“Jan”,”January”,”Janus”,”Bob” -in “Jan”

“Jan”,”January”,”Janus”,”Bob” -in “Jan”,”January”,”Janus”,”Bob”

这两个命令都返回False,需要知道引用值和测试值要位于-in和-notin操作符的两端。需要确保-in和-contains都有一个测试值,将会得到一个布尔值True或False。

4.8、使用-replace操作符

有时得到的数据可能需要将输入值更改为其他值,例如,需要将服务器的名称值从“Phx-Ser-01”更改为“SEASRV-01”,可以这样做:

“Phx-Ser-01” -replace “PHX-SER”,”SEASRV”          结果为:SEASRV-01

格式为:InPutString -replace “Matchme”,”Replacement”

替换的大小未必相同:”ABCDEFG” -replace “de”,”ILOVECOOKIES”     返回的结果是:ABCILOVECOOKIESFG

还可以使用-replace删除替换值:“ABCDEFG” -replace “de”             返回的结果是:ABCFG

五、使用变量

通常使用变量来存储命令的结果,并向其他命令提供输入。变量标识为以$开头的文本字符串,变量本身是一个文本字符串,不区分大小写,可在变量名中包含各种字符。变量名可包括空格和特殊字符,在变量名中放置空格和特殊字符会导致大量混乱,PowerShell将拒绝任何包含空格或特殊字符的变量名,下划线(_)字符除外。如果坚持使用字符,如连字符或空格,则需要将命令括在括号中:

${my poorly-chosen variable name} = “This is a really bad idea.”

有时,必须引用一个可能有特殊字符的变量,例如环境变量${ENV:ProgramFiles(x86)},如果需要一个列表,使用Get-ChildItem:Get-ChildItem ${ENV:ProgramFiles(x86)}

5.1、清理和删除变量

要删除变量的值,可简单地将其值设置为$null,也可使用Clear-Variable cmdlet:

$removeMyValue = $NULL

Clear-Variable -Name removeMyValue

这些变量仍然存在,它们的值被赋值为$Null,它们仍占用空间。

如果想删除一个变量,可使用如下命令:

Remove-Variable -Name removeMyValue

这将清除该值并从内存中删除该变量,注意作用域,如果删除局部作用域的变量,将只清除局部作用域的变量;如果有一个同名的父变量,将在局部作用域中看到该变量。

5.2、使用环境变量

PowerShell在另一个名为env:的PowerShell驱动器中存储环境变量,这用于存储Winodws安装目录、用户目录和临时目录的位置等信息。要查看此目录的内容,可执行相同的操作:

Set-Location env:

Dir

Get-ChildItem env:

Get-Item env

env:驱动器中的这些对象没有子项,因此Get-Item和Get-ChildItem返回相同的信息。(找不到路径“env:”,因为该路径不存在。)

环境变量由父会话和子会话共享,这允许在父会话和子会话之间共享值,在变量名前加上$env:可查看和操作环境变量:

$env:TMP

$windowsdirectory = $env:windir

$env:myNewEnvironmentalVariable = "Data that is available to the parent and child"

六、使用函数

6.1、函数的执行

函数是一个给定名称的代码块,函数的基本格式:

Function Snag-SecurityLog {Get-EventLog Security}

这个命令创建一个名为Snag-SecurityLog的函数,可创建一个别名来调用函数:

New-Alias -Name View-SecurityLog -Value Snag-SecurityLog

现在输入View-SecurityLog来调用Snag-SecurityLog函数,该函数运行Get-EventLog Security。

6.2、Splatting

Splatting可以修改别名,以允许向函数传递参数,而不必声明参数,甚至不必声明参数的数量。如下:

Function View-ALog {Get-EventLog @Args}

New-Alias -Name Grap-Log -Value View-ALog

现在修改了函数和别名,可使用单个别名并向函数传递参数,这个新的别名可告诉函数要检索哪个日志,例:

Grap-Log Security

Grap-Log Application

Grap-Log System

6.3、创建函数

函数可以非常简单,如果想知道PowerShell使用了多少RAM并转换为MB,可以使用一个函数:

function Pull-ShellRAM {

    $powershellProcess = Get-Process -Name "powershell" -ErrorAction SilentlyContinue

    if ($powershellProcess) {

        $workingSetMB = $powershellProcess.WorkingSet64 / 1MB

        Write-Output "PowerShell Process Working Set Size: $workingSetMB MB"

    } else {

        Write-Output "PowerShell process not found."

    }

}

Pull-ShellRAM

函数可以任意命名,但最好遵循PowerShell目前使用的标准动词-名词约定,动词应该表示函数在执行什么动作,这个名词应该识别出操作的数据项。注:避免使用已有的cmdlet给函数命名,如果使用一个已在使用的名称,该名称将掩盖原来的cmdlet,将改而调用现在的函数。

这个函数可以保存参数,若要添加几个参数,用逗号来分隔,如果在声明函数时声明了参数,则不能在函数体中声明任何其他参数。用一个大括号({)打开函数体,并开始添加语句。begin、process和end在管道中使用。在大括号之后,可添加任意数量的语句,如果语句在同一行上,则需要在语句之间添加分号(;),还可以将每个语句放在单独的行上,这样就不必使用分号;也可以缩进各个部分,以使它们更便于阅读。缩进时,可以全部使用空格或全部使用制表符(TAB键);如果同时使用空格和制表符(TAB键),在脚本之间剪切和粘贴时,可能导致脚本认为空格标识一个参数。如果对缩进全部使用空格或全部使用制表符,就不会出现这个问题。

为提高可读性,可为脚本增加注释,这些注释前面有一个#符号,那一行上#符号右的任何东西都被忽略;如果希望注释多行,则需要在每行的前面使用#符号。使用小于号和英镑符号(<#)来启动块,就可以创建一个注释块,然后,可根据需要在任意行中添加任意数量的注释;然后用另一个#符号和一个大于号(#>)结束注释块。

<#

这是一个带有参数和多行注释的函数示例

该函数接受两个参数$num1和$num2,计算它们的和,并输出结果

#>

# 定义一个名为Calculate-Sum的函数,接受两个参数$num1和$num2

function Calculate-Sum {

    param (

        [int]$num1,

        [int]$num2

    )

    # 计算两个参数的和

    $result = $num1 + $num2

    # 输出结果

    Write-Output "参数 $num1 和 $num2 的和是: $result"

}

# 调用函数并传递参数

$num1 = 5

$num2 = 10

Calculate-Sum -num1 $num1 -num2 $num2  # 输出:参数 5 和 10 的和是: 15

6.4、使用参数

6.4.1、命名参数

命名参数有一个名称,可以赋予一个值或者一个值数组,可在包含代码区域的大括号内部或外部命名它们,下面是一个声明函数时声明参数的例子:

Function DisPlay-Values($Parameter1,$Parameter2)

    {

     $var1 = $Parameter1

     Write-Host ("This is from var1 "+$var1)

     Write-Host ("This is from Parameter2 "+$Parameter2)

     Write-Host ($Parameter1,$Parameter2)

}

可将参数的值赋给变量,也可以直接引用参数。

要使函数工作,必须将代码加载到会话中;通常,函数代码会保存为.ps1文件,也称为脚本,然后加载并运行脚本;将函数放入当前作用域的Function:驱动器,将函数加载到会话中。按如下方式调用此函数并传递参数:

DisPlay-Values -Parameter1 Hello -Parameter2 World

在每个传递的参数之间有一个空格,当然,也可以直接省略参数名,根据定义的顺序确定其位置,例如:

DisPlay-Values Hello World

如果用逗号传递参数值,所有值都将作为数组添加到此参数中。

如果决定在函数体中声明参数,那么在声明函数本身时就不能声明它们;注意,参数名只是名称,参数的位置与与声明时的位置定义,传递的第一个参数实际上位于位置0.

运行函数是相同的,除非在调用函数时将值传递给参数,否则定义的任何参数(不发送参数值或赋予默认值)都赋值为$NULL。如果传递了一个具有默认值的参数,所传递的值将覆盖默认值。下面举例说明:

Function DisPlay-Values

    {

    Param(

            $Parameter1,$Parameter2,

            $Parameter3,

            [String]$Parameter4 = "Nano",

            $Parameter5

        )

     Write-Host ($Parameter3,$Parameter4,$Parameter1,$Parameter2,$Parameter5)

}

当参数的顺序不同时,需要按以下方式传递参数,使它成为合理的语句,而不是覆盖默认值:

DisPlay-Values Server 2022 Windows -Parameter5 Installation

6.4.2、必选参数

可在[Parameters]中定义与参数关联的属性,一个属性是Position,另一个是Mandatory。

Function Show-OurValues

    {

    Param(

            $Param1,

            [Parameter(Mandatory = $True)][String]$StringParam

        )

     Write-Host ($Param1,$StringParam)

}

因为只传递了第一个参数(位于位置0),而第二个参数(位于位置1)是必选的,所以控制台要求输入第二个值。必须使用[Parameter(Mandatory=$True)] $myParameter来修改参数的属性,也可设置其他属性,比如它的的位置:

[Parameter(Mandatory = $True,Position=1)]$myParameter

这个示例将$myParameter设置为第二个参数,位置从0开始,该参数也是必需的;如果通过名称引用命名参数,PowerShell将忽略命名参数的位置,并直接赋值。

6.4.3、位置参数

所创建的参数将根据定义它们的顺序来分配位置,或硬编码其位置,默认情况下,所有参数都是位置参数,但可用另一种方式存储它们。不是通过@传递参数,而是将它们放入$args数组中。

传递给函数的任何内容都存储在$args数组中,第一个参数在第一个位置,从0开始;$args数组其中不仅有值,还有其他属性,而位置就是行。第一个参数存储在$args[0]中,下面是示例:

Function Add-Domain

{

$FQDN = $args[0] +".Contoso.com"

$FQDN

}

如果没有传递参数,$args[0]将是$NULL,l输出如下图:

6.4.4、开关参数

开关参数就像像电灯开关,其思想是,如果传递了开关参数的值,无论传递的实际值是多少,参数都将变成$True,除非传递的是$False。当参数定义为开关参数时,该参数变默认为$False。

如果在调用函数时没有发送开关参数,它就一直是$False,如果将参数传递给函数,即使没有值,PowerShell也会将参数值设置为$True;同样可将参数传递为$False,它将始终是$False。

Function Check-Domain

{

param (

        [switch]$DomainParam   #This is -DomainParam to $False

        )

If ($DomainParam -eq $true) {"There is a domain."}

else {"No Domain Found."}

}

在参数名之后添加一个布尔值,可将布尔值传递给参数:

在PowerShell中,开关参数(Switch Parameter)是一种特殊类型的参数,它不需要显式地传递参数值。如果命令或函数定义了开关参数,当您在调用命令或函数时,如果提供了这个参数,它将被视为“开启”($trueTrue);如果没有提供这个参数,它将被视为“关闭”($falseFalse)。

在定义函数时,您可以使用 Switch 参数类型来声明开关参数。

开关参数通常用于控制脚本或函数的行为,特别是当您需要在脚本或函数中启用或禁用特定的功能时。如果您的脚本或函数需要输出详细信息(verbose output),您可以使用 开关参数来控制是否输出这些详细信息。只有在用户明确指定 时,脚本或函数才会输出详细信息。

6.5、将管道对象发送给带有Begin、Process和End的函数

可将管道对象发送给函数,只在函数开始时执行Begin语句,此时还没有从管道里提取出任何数据,一旦Begin语句完成,Process语句将为管道中的每个对象运行一次;当对象分配管道时,它们由$PSItem自动变量和旧的$_自动变量引用,这两个自动变量都引用PowerShell管道中的当前对象,$PSItem只由PowerShell 3.0或更高版本支持。

  • Begin:在管道对象被传递到函数之前执行。它只执行一次,通常用于初始化操作。
  • Process:在管道对象被传递到函数后,为管道中的每个对象执行一次。这是处理大量数据的主要部分。
  • End:在所有管道对象被处理后执行,通常用于清理操作。

Function Examine-Pipeline

{

   

    Begin {$myVar = "Nothing first pulled from the pipeline --->$PSItem<---"

           $myVar

    }

    Process {

           $myVar = "Value from the pipeline $PSItem"

           $myVar

    }

    End {

          $myVar = "This only executes at the end"

          $myVar

    }

}

在这个例子中,函数的 Begin 部分输出了初始化文本,并将其赋值给 $myVar 变量。Process 部分在管道中的每个对象上执行一次,并输出每个对象的文本,然后将文本赋值给 $myVar 变量。End 部分在所有对象处理完成后执行,并输出指定的结束文本。

查看会话中的所有函数:Get-ChildItem -Path Function:

七、使用循环

有时在编写脚本时,可能需要反复执行相同的操作,但要使用不同的对象。可能有一个管道,其中充满了需要操作的对象,还可能需要一次次地重新填充管道。创建各种循环和条件就可以实现这一点,循环把单个命令和变量元素真正结合在一起。

7.1、使用For循环

For循环将代码块运行特定的次数,适用于反复运行相同的代码,或处理与特定特征匹配的数组成员;如果想对数组的所有成员执行相同的操作,可以使用Foreach循环。

For循环语法:

For (<init>; <condition>; <repeat>)

        {<statement list>}

For循环从具有一个或多个命令的<init>部分开始,如果使用多个命令,则需要用逗号分隔命令;这个部分用初始值初始化一个变量,该变量用于跟踪循环执行的次数。

<condition>部分具有某种类型的布尔比较条件。这通常是为了查看循环执行的次数是否足够多,如果此结果为True,则<statement list>部分运行一次,然后运行<repeat>部分中的命令。

<repeat>部分通常用于递增init部分中设置的变量,然后执行<condition>部分;如果条件仍然为真,则<statement list>中的语句(称为命令块)将再次运行,repeat部分将再次运行,这将重复执行,直到条件的值为$False,这种情况下,For循环结束。

<statement list>部分包含每次循环条件求值为True时执行的代码,还可在<statement list>部分中更改<condition>部分中要测试的变量。

<init>、<condition>和<repeat>部分用分号分隔,也可使用回车分隔,必须有这三个部分,用圆括号括起来,<statement list>部分必须有一个命令,例如:

For ($i=1 ; $i -lt 3; $i++) {"This counter is at $i"}

第一次迭代时,$i设置为1;进行比较时,$i小于3,因此可运行命令块,输出结果;然后进入repeat部分,将$i递增1,例$i=2。如果想将$i递增1以上的值,可以指定$i+=5,这使$i的值增加5.

再次测试该条件,这次$i=2,所以比较的结果仍然为True,代码块再次运行,之后$i递增到3;再次进行比较时,发现值为3的$i不再小于3,因此For循环结束。

7.2、使用Foreach循环

Foreach循环用于遍历数组的所有成员,这个循环对每一条目运行命令,条目由不需要声明的变量标识,这个变量表示数组中的每个条目,一次表示一个条目;与For循环不同,Foreach不需要知道循环运行的次数,也不需要对计数变量进行任何初始化。语法:

Foreach ($<item> in $<collection>) {<statement list>}

  1. 可设置一个数组,运行整个处理过程,本例使用一个仅为Foreach循环创建的变量:

$ourCityArray = "BeiJing","ShanDong","HeNan","HeBei"

Foreach ($city in $ourCityArray) {

    Write-Output "The City here is $city"

}

Write-Output "There are no more cities"

  1. 定义了一个包含四个城市名字的数组 $ourCityArray,城市名字分别是 "BeiJing"、"ShanDong"、"HeNan" 和 "HeBei"。
  2. 使用 foreach 循环遍历 $ourCityArray 中的每个城市名字。在每次循环中,变量 $city 会被赋值为数组中的一个城市名字。
  3. 在循环内部,使用 Write-Output 命令输出文本 "The City here is",后面紧跟着当前循环迭代到的城市名字。例如,第一次循环时,输出 "The City here is BeiJing",第二次循环时,输出 "The City here is ShanDong",以此类推。
  4. 当循环完成遍历数组中的所有城市名字后,脚本会执行 Write-Output 命令输出文本 "There are no more cities",表示所有城市都已经遍历完毕。

  1. 使用Get-ChildItem查看作用域内以字母G开头的所有函数:

Foreach ($Functions in Get-ChildItem -Path Function: -Name -Include G*) {$Functions}

  1. Get-ChildItem -Path Function: -Name -Include G*:这部分代码使用Get-ChildItem命令从Function驱动器中获取所有函数的名称。-Path Function:指定了查找的路径为Function驱动器,-Name参数表示只获取对象的名称,而-Include G*表示只包括那些以字母"G"开头的对象。
  2. Foreach ($Functions in ...):这是一个foreach循环,它遍历了Get-ChildItem命令返回的所有符合条件的函数名称。
  3. { $Functions }:在每次循环中,$Functions变量会被输出,即输出符合条件的函数的名称。

  1. 在命令管道中包含Foreach,不需要已识别的变量或数组,从前一个命令提供的管道值中提取每个项,并对每个项运行语句项:

Get-ChildItem -Path ENV: -Include "*Win*" -Name | Foreach {"ENV:$PSItem"}

Get-ChildItem -Path ENV: -Include "*Win*" -Name 查找的是环境变量(Environment Variables)中名称包含 "Win" 的变量。在Windows系统中,windir 是一个系统环境变量,表示Windows操作系统的安装目录路径。在 Foreach 循环中,$_$PSItem 变量代表当前循环到的环境变量的名称。在这里,字符串 "ENV:$PSItem" 使用双引号括起来,其中 $PSItem 会被替换为当前循环到的环境变量的名称,然后字符串前面加上 "ENV:"。因此,这一部分的作用是将每个符合条件的环境变量的名称前面添加 "ENV:",最终输出的结果是符合条件的环境变量的完整路径,以 "ENV:" 为前缀。所以,当执行 Get-ChildItem -Path ENV: -Include "*Win*" -Name 命令时,会找到 windir 这个环境变量的名称,然后在 Foreach 循环中,"ENV:" 字符串会被添加到这个环境变量的名称前面,最终输出的结果就是 "ENV:windir"。

  1. 可以使用-Begin、-Process和-End命令块,这类似于前面函数块的Begin、Process和End部分;从管道中提取对象前,只处理-Begin部分,每个项目执行一次-Process块,在处理完管道中的所有对象后,只执行一次End块。例:

$ourCityArray = "BeiJing","ShanDong","HeNan","HeBei"

$ourCityArray | ForEach-Object -Begin {Write-Host("AD Site Cities")} -Process {Write-Host ($PSItem)} -End {Get-Date}

  1. $ourCityArray = "BeiJing","ShanDong","HeNan","HeBei":这行代码定义了一个包含四个城市名字的数组 $ourCityArray
  2. $ourCityArray | ForEach-Object -Begin {Write-Host("AD Site Cities")}:这部分使用了管道 | 将数组 $ourCityArray 的内容传递给 ForEach-Object 命令。-Begin 参数用于在处理对象之前执行的脚本块,在这里,它会输出 "AD Site Cities"。
  3. -Process {Write-Host ($PSItem)}-Process 参数用于处理每个输入对象的脚本块。在这里,$PSItem 代表了当前循环到的城市名字,它会被输出到控制台。
  4. -End {Get-Date}-End 参数用于在所有输入对象处理完后执行的脚本块。在这里,它会输出当前的日期和时间(使用 Get-Date 函数获取)。

7.3、使用If语句

If语句用于根据布尔条件的测试结果运行代码块,If语句提供了三个选项的多个组合,这些选项也支持多层嵌套:

  1. 如果条件测试结果计算为$True,就运行一个代码块。
  2. 如果条件测试结果计算为$True,之前所有条件都计算为$False,就运行一个代码块。
  3. 如果之前所有条件都计算为$False,就运行一个代码块。

语法:

If (<test1>) {<statement list 1>} [elseif (<test2>)] {<statement list 2>} [else {<statement list 3>}]

例如:

If ($a -eq 3) {Write-Host "$a equals 3."}

只有当结果为True,才会执行代码块;还可以使用如下If语句:如果条件为True,就运行一个代码块,如果条件为False,则运行另一个代码块。

If($a -gt 3)

{

    Write-Host "Variable a is greater than 3"

}

Else

{

    Write-Host "Variable a is less than 3 or Variable a is empty."

}

只有当前面的if语句为$False时,该代码的Else部分才会运行;可能是因为$a变量小于等于3,也可能是$a是$Null。

如果想让Else语句在运行代码块之前测试另一个条件,可将它放入另一个If语句,对于Elseif,只有当第一个条件计算为$False时,才会计算第二个条件。

If ($a -lt 10)

{

    Write-Host "Site Link cost is less than 10."

}

Elseif ($a -eq $Null)

{

    Write-Host "Site Link cost is Null."

}

Elseif ($a -lt 21)

{

    Write-Host "Site Link cost is between 10 and 20."

}

Else

{

    Write-Host "Site Link a is greater than 20."

}

  1. If ($a -lt 10):如果变量 $a 的值小于 10,执行以下代码块。
    • 输出消息:"Site Link cost is less than 10."
  2. Elseif ($a -eq $Null):如果变量 $a 的值为 $Null(即空值),执行以下代码块。
    • 输出消息:"Site Link cost is Null."
  3. Elseif ($a -lt 21):如果变量 $a 的值小于 21,且不满足前面的条件,执行以下代码块。
    • 输出消息:"Site Link cost is between 10 and 20."
  4. Else:如果变量 $a 的值不满足前面的任何条件,执行以下代码块。
    • 输出消息:"Site Link a is greater than 20."

7.4、使用Switch语句

Switch语句不像Switch参数,Switch语句指定一个测试值,然后包含多个条件,如果测试值与条件匹配,则执行关联的操作;与Else语句不同的是,所有Switch条件通常都要测试,语法:

Switch (<test-value>)

    {

    <Reference-value> {<action>}

    <Reference-value> {<action>}

    }

  1. 测试值是根据每个参考值来检查的,即使测试值与同一Switch块中的前一个参考值相匹配,也要检查;如果测试值匹配参考值,就执行动作代码块;如果测试值不匹配任何Switch引用值,就不会为该测试值执行任何块。

Switch (7)

{

    2  {"This matches the reference value two"}

    94 {"This matches ninety-four"}

    7  {"This is matching seven"}

    4  {"This matches four"}

    7  {"This matched seven again"}

 }

  1. Switch (7):这里指定了要被匹配的值是7。
  2. Switch语句运行时,它会按顺序检查每个条件,看是否匹配输入的值。
  3. 在你的Switch语句中,存在两个条件匹配了输入值7:
    • 第一个匹配项是 7 {"This is matching seven"}:这个条件匹配到了值为7,所以它会执行代码块 "This is matching seven",输出该消息。
    • 第二个匹配项是 7 {"This matched seven again"}:虽然前面已经有一个匹配项,但是这个条件也匹配到了值为7。由于Switch语句只会执行第一个匹配项,因此这个条件的代码块不会被执行。
  1. 处理数组,Switch值的顺序可以任意,但每个测试值都用每个参考值来测试,即使测试值与相同Switch块中的其他参考值匹配,也要测试。

Switch ("BeiJing","ShanDong","HeBei")

{

    TianJin  {"This matches TianJin"}

    BeiJing {"This matches BeiJing"}

    ShanDong  {"This is matches ShanDong"}

    HeNan  {"This matches HeNan"}

    HeBei  {"This matches HeBei"}

 }

  1. 如果要防止一个测试值匹配多个参考值,可在Switch上添加Break

Switch ("BeiJing","ShanDong","HeBei")

{

    TianJin  {"This matches TianJin"}

    BeiJing {"This matches BeiJing";Break}

    ShanDong  {"This is matches ShanDong"}

     BeiJing {"This matches BeiJing again"}

    HeNan  {"This matches HeNan"}

    HeBei  {"This matches HeBei"}

 }

执行Switch块时,如果在Switch引用值中遇到Break语句,Switch块就停止计算测试值并退出,即使测试值匹配同一个Switch块中后面的引用值,或者有更多要检查的测试值,也是如此。

  1. 如果希望停止对特定测试值的进一步处理,而去处理其他测试值,可以使用Continue语句

Switch ("BeiJing","ShanDong","HeBei")

{

    TianJin  {"This matches TianJin"}

    BeiJing {"This matches BeiJing";Break}

    ShanDong  {"This is matches ShanDong"}

     BeiJing {"This matches BeiJing again"}

    HeNan  {"This matches HeNan"}

    HeBei  {"This matches HeBei"}

 }

  1. 还可标记一个默认的Switch块,如果测试值不匹配其他任何条件,就使用它

使用一个数组 ("BeiJing","ShanDong","HeBei") 作为匹配值,然后对这些值进行条件匹配。但是,Switch 语句通常用于匹配一个单一的值,而不是一个数组。

但正确的应该是,如果你想要匹配数组中的每个元素,你可以使用 Foreach-Object 循环来遍历数组,并在循环内部使用 Switch 语句进行匹配。以下是优化示例:

$cityArray = "BeiJing","ShanDong","HeBei"

$cityArray | ForEach-Object {

    switch ($_){

        "GuangDong" {Write-Output "This matches TianJin"}

        "ShanXi" {Write-Output "This matches BeiJing"}

        "AnHui" {Write-Output "This matches ShanDong"}

        "HeNan" {Write-Output "This matches HeNan"}

        "HuBei" {Write-Output "This matches HeBei"}

        default {Write-Output "No match found for $_"}

    }

}

在这个示例中,ForEach-Object 循环遍历了数组 $cityArray 中的每个元素,并将每个元素作为 $_ 变量传递给 Switch 语句进行匹配。如果匹配成功,会输出相应的消息。如果没有匹配项,则会执行 default 分支,并输出 "No match found for $_",其中 $_ 是当前循环到的元素。

  1. 每个Switch语句块中只能有一个default语句,每个Switch语句块必须包含至少一个条件语句。每个 Switch 语句块中只能有一个 default 语句: default 语句用于在没有匹配任何条件的情况下执行的代码块。一个 Switch 语句块只能包含一个 default 语句。
  2. 每个 Switch 语句块必须包含至少一个条件语句: Switch 语句必须包含至少一个条件语句,用于匹配测试值。没有条件语句的 Switch 语句是无效的。
  3. Switch 语句的参数: Switch 语句的参数可以是正则表达式、通配符或精确值。这些参数用于定义匹配条件。在PowerShell中,你可以使用 -regex 参数指定正则表达式匹配,使用 -wildcard 参数指定通配符匹配,或者使用精确值进行匹配。
  4. 只使用一个参数: Switch 语句只能使用一个参数。如果指定了多个参数,只会使用最后一个指定的参数。
  5. 区分大小写: 默认情况下,Switch 语句在匹配时是不区分大小写的。但是,你可以使用 -caseSensitive 参数来强制区分大小写。

示例:

$dayOfWeek = "Monday"

Switch ($dayOfWeek) {

    "Monday" { Write-Output "It's the start of the workweek." }

    "Tuesday" { Write-Output "It's still early in the workweek." }

    "Wednesday" { Write-Output "It's the middle of the workweek." }

    "Thursday" { Write-Output "It's almost the end of the workweek." }

    "Friday" { Write-Output "It's the end of the workweek. TGIF!" }

    "Saturday" { Write-Output "It's the weekend. Time to relax!" }

    "Sunday" { Write-Output "It's the weekend, but tomorrow is Monday." }

    default { Write-Output "Invalid day of the week." }

}

7.5、使用While循环

使用While循环比使用For循环更简单,语法:

While (<Condition>) {<statement list>}

只要Condition为True,PowerShell就会无休止地遍历<statement list>;在块的末尾,再次计算Condition,如果Condition不再为True,则循环结束。注意,Condition仅在每个循环的开始处计算,如果有多个语句临时使条件为为False,并且在完成循环之前条件变成True,则继续循环。示例:

While ($count -ne 5)

{

$count++

Write-Host "The count is "$count

}

  1. 初始化变量 $count,通常在这段代码之前会有 $count = 0 或者 $count = 1 的语句来给 $count 赋初始值。
  2. while ($count -ne 5):这个条件表示,只要 $count 的值不等于5,就会一直执行循环内部的代码块。
  3. $count++:每次循环迭代,$count 的值会增加1。
  4. Write-Host "The count is " $count:这行代码会输出当前 $count 的值。Write-Host 用于将信息输出到控制台。
  5. 循环会一直执行,直到 $count 的值等于5。一旦 $count 的值等于5,循环就会结束。

注意:第一次运行代码时,变量$count是$Null(在 PowerShell 中,如果你在代码中使用了一个未初始化的变量(即变量没有被赋初值),它的默认值会是 $null。)。

如果想在一行代码中中编写While循环,应该用分别分隔不不同的语句行。

While ($count -ne 5){$count++;Write-Host "The count is "$count}

7.6、使用Where-Object方法

If语句非常强大,但要编写大量代码,希望基于属性值选择集合中的对象时,可使用Where语句。

在 PowerShell 中,你可以使用 Where-ObjectWhereWhere-Object 的别名)两种方法来进行条件筛选。

Where-Object 命令: Where-Object 是 PowerShell 中用于过滤对象集合的命令。它可以接受一个脚本块(Script Block)作为参数,根据脚本块中的条件筛选输入的对象,并返回符合条件的对象集合。例如:

$numbers = 1, 2, 3, 4, 5, 6

$filteredNumbers = $numbers | Where-Object { $_ -gt 3 }

Write-Output $filteredNumbers

Where(Where-Object 的别名): WhereWhere-Object 的别名,你可以直接使用 Where 关键字来调用 Where-Object 命令。

;