batch脚本在面对xml读写、字符串处理等需求时常常捉襟见肘,无法满足我们的需求,因此掌握powershell显得非常必要。
下面这些命令可以帮助你了解命令作用及其用法。
注意:
- powershell与batch一样不区分大小写
- 变量采用小驼峰命名
- 函数采用大驼峰命名,一般格式为:动词-名词
- powershell脚本文件后缀为ps1,文件名称按大驼峰命名或者下划线均可没有这方面的规范
- powershell兼容cmd因此cmd的一些规则也适用于powershell
- powershell一切皆对象,命令本身也是对象,比如:$m=或者
.day
[System.Enum]::GetNames([Microsoft.PowerShell.ExecutionPolicy])
:获取系统全部执行策略总结有以下几种方式:
echo Write-Host "hello powershell" | powershell.exe -noprofile -
# 原理实质上跟2是一样的
# 在powershell中可以使用get-content读取文件,在cmd中使用type
type D:\my_script.ps1 | powershell.exe -noprofile -
# iex是Invoke-Expression命令的简写,下面会介绍
powershell -nop -c "iex(New-Object Net.WebClient).DownloadString('http://192.168.1.2/my_script.ps1)"
-Command
命令参数# 此方法不需要一个交互式的窗口
# 适用于简单脚本的执行,对于复杂脚本会发生解析错误
# 不会将内容写到磁盘中
# 可以将该命令写到一个bat文件中,然后放到启动目录中,来帮助提权
powershell -command "write-host 'hello powershell'"
-EncodedCommand
命令参数# 脚本内容为Unicode/base64 encode的字符串,可以弥补-command对于长脚本解析问题的短板
# 该工具套件还包括一个小的压缩方法来减少由于encode后字符串太长的情况
$bytes=[System.Text.Encoding]::Unicode.GetBytes("write-host 'hello powershell'")
$encodedCmd=[Convert]::ToBase64String($bytes)
powershell.exe -encodedCommand $encodedCmd
-ExecutionPolicy
命令参数修改执行策略ByPass
:什么都不做,什么警告也不提示Unrestricted
:加载所有的配置文件和执行所有的脚本,如果你运行一个从网上下载的未签名的脚本,会给出权限提示Remote-Signed
:允许执行远程已签名脚本,脚本签名方法详见签名指南AllSigned
:允许本地和远程已签名脚本powershell -executionpolicy bypass -file .\my_script.ps1
Set-ExecutionPolicy
命令修改执行策略注意:在 Windows Vista 和 Windows 的更高版本中,若要运行更改本地计算机(默认)的执行策略的命令,请使用“以管理员身份运行”选项启动 Windows PowerShell。
示例:指定临时执行策略生效范围为当前进程
# 先修改执行策略为Bypass,生效范围是当前会话进程
Set-ExecutionPolicy Bypass -Scope Process
# 再执行脚本
.\my_script.ps1
# 删除刚刚指定的执行策略
Set-ExecutionPolicy Undefined -Scope Process
示例:指定临时执行策略生效范围为当前用户
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted
# 再执行脚本
.\my_script.ps1
Invoke-Command
命令# 执行简单脚本(脚本块可以换行)
invoke-command -scriptblock {write-host "hello powershell."}
# 从远程服务器抓取执行策略运用到本机
invoke-command -computername your-server-name -scriptblock {get-executionpolicy} | set-executionpolicy -force
Invoke-Expression
命令get-content .\my_script.ps1 | invoke-expression
# 上述命令可以缩写为下面
gc .\my_script.ps1 | iex
注意:
- 可以通过get-alias命令查看指令所有的别名,例如:get-alias -definition get-childitem
- 可以通过Set-Alias或New-Alias为命令设置新的别名
# 定义替换AuthorizationManager的函数
function Disable-ExecutionPolicy{
($ctx = $executioncontext.gettype().getfield("_context","nonpublic,instance").getvalue($executioncontext)).gettype().getfield("_authorizationManager","nonpublic,instance").setvalue($ctx, (new-object System.Management.Automation.AuthorizationManager "Microsoft.PowerShell"))
}
# 调用该函数
Disable-ExecutionPolicy
# 执行脚本
.\my_script.ps1
由于自动化操作往往伴随系统风险,因此powershell的默认设置会将自动化的操作归类为危险操作,在执行这些风险操作之前系统会要求用户进行确认。
-confirm
参数这一标准的设置被存储在全局变量$ConfirmPreference
中。$ConfirmPreference
可以对默认设置或者其它更严格的设置作出判断与回应。
# 查看当前的系统配置
PS> $ConfirmPreference
High
# c查看所有的可选项
PS> [ENUM]::GetNames($ConfirmPreference.getType())
None
Low
Medium
High
当$ConfirmPreference
的值设置为“None”时,Powershell就不会进行操作前询问确认,即使可能面临高风险的操作。
也可以在cmdlet指令后增加-confirm
参数,为该指令增加用户确认步骤:
Stop-Process -Name *cm* -Confirm
确认
是否确实要执行此操作?
对目标“cmd (1012)”执行操作“Stop-Process”。
[Y] 是(Y) [A] 全是(A) [N] 否(N) [L] 全否(L) [S] 挂起(S) [?] 帮助 (默认值为“Y”): ?
-whatif
参数通过-whatif
参数你可以进行试运行许多cmdltes,Powershell不会执行任何对系统有影响的操作,只会告诉你如果没有模拟运行,可能产生什么影响和后果。
示例:
Stop-Process -Name *a* -WhatIf
WhatIf: 对目标“AcroRd32 (4544)”执行操作“Stop-Process”。
如果你想让自己的脚本和函数也支持模拟运行,只需要进行简单的整合。多增加一个switch参数:
function MapDrive([string]$driveletter, [string]$target, [switch]$whatif){
If ($whatif) {
Write-Host "WhatIf: creation of a network drive " + "with the letter ${driveletter}: at destination $target"
} Else {
New-PSDrive $driveletter FileSystem $target
}
}
# 首先进行模拟运行:
MapDrive k 127.0.0.1c$ -whatif
WhatIf: creation of a network drive
with letter k: at destination 127.0.0.1c$
# 执行命令
MapDrive k 127.0.0.1c$
Name Provider Root
---- -------- ----
k FileSystem 127.0.0.1c$
通常调试代码有两种手段:
虽然write-host可以输出日志信息,但其实powershell有专门的指令实现这一功能:write-debug
,我们可以修改$DebugPreference
设置项来改变debug日志行为:
枚举值 | 描述 | 示例 |
---|---|---|
SilentlyContinue |
默认,调试关闭 | |
Stop |
输出调试信息,终止脚本执行 | |
Continue |
输出调试信息,继续执行脚本 | |
Inquire |
输出调试信息,询问用户是否继续执行 |
PS C:> $DebugPreference="silentlycontinue"
PS C:> Write-Debug "输入一行调试信息" ; Write-Host "伦敦奥运会女子体操决赛"
伦敦奥运会女子体操决赛
PS C:> $DebugPreference="stop"
PS C:> Write-Debug "输入一行调试信息" ; Write-Host "伦敦奥运会女子体操决赛"
调试: 输入一行调试信息
Write-Debug : 已停止执行命令,因为首选项变量“DebugPreference”或通用参数被设置为 Stop。
所在位置 行:1 字符: 12
+ Write-Debug <<<< "输入一行调试信息" ; Write-Host "伦敦奥运会女子体操决赛" + CategoryInfo : OperationStopped: (:) [Write-Debug], ParentContainsErrorRecordExceptio + FullyQualifiedErrorId : ActionPreferenceStop,Microsoft.PowerShell.Commands.WriteDebugCommand
PS C:> $DebugPreference="continue"
PS C:> Write-Debug "输入一行调试信息" ; Write-Host "伦敦奥运会女子体操决赛"
调试: 输入一行调试信息
伦敦奥运会女子体操决赛
PS C:> $DebugPreference="inquire"
PS C:> Write-Debug "输入一行调试信息" ; Write-Host "伦敦奥运会女子体操决赛"
调试: 输入一行调试信息
确认
是否继续执行此操作?
[Y] 是(Y) [A] 全是(A) [H] 终止命令(H) [S] 挂起(S) [?] 帮助 (默认值为“Y”): y
伦敦奥运会女子体操决赛
可以在 Powershell ISE 中通过F9断点执行Powershell脚本。
即使没有ISE也可以单步跟踪,做法如下:
Set-PSDebug -step,Powershell会每只行一段代码,就会向用户询问是否继续执行
在Powershell控制台的许多更改只会在当前会话有效。一旦关闭当前控制台,你自定义地所有别名、函数、和其它改变将会消失,除非将更改保存在windows环境变量中。我们需要profile来保存一些基本的初始化工作。
profile脚本也是ps1脚本,Powershell在启动时会执行对应的profile脚本进行初始化。
参考文章:Powershell自动执行脚本之profile
符号 | 含义 | 示例 |
---|---|---|
# |
行注释 | |
` | 换行符代码截断 | |
| |
管道符 | $text= dir | Out-String dir | Out-File .temp.txt |
> |
重定向符,标准输出流:> ,异常输出流:2> 重定向符的作用与 Out-File 参数的功能非常接近,但是Out-File 功能丰富,出了指定文件名,可以-encoding 指定字符编码 |
Get-Item "NoSuchDirectory" 2> Error.txt |
PS C:Powershell> (3,4,5 ) -contains 2
False
PS C:Powershell> (3,4,5 ) -contains 5
True
PS C:Powershell> (3,4,5 ) -notcontains 6
True
PS C:Powershell> 1,2,3,4,3,2,1 -eq 3
3
3
PS C:Powershell> 2 -eq 10
False
PS C:Powershell> "A" -eq "a"
True
PS C:Powershell> "A" -ieq "a"
True
PS C:Powershell> "A" -ceq "a"
False
PS C:Powershell> 1gb -lt 1gb+1
True
PS C:Powershell> 1gb -lt 1gb-1
False
运算符 | 描述 | 示例 |
---|---|---|
-and |
和 | $true -and $true |
-or |
或 | $true -or $true |
-xor |
异或 | $true -xor $false |
-not |
逆 | -not $true |
在其它编程语言中喜欢将反斜杠作为转义字符,但是在Powershell中扮演转义字符角色的不是反斜杠,而是反引号“`”字符串中的反引号。
转移字符 | 描述 | 示例 |
---|---|---|
`n | 换行符 | |
`r | 回车符 | |
`t | 制表符 | |
`a | 响铃符 | |
`b | 退格符 | |
`’ | 单引号 | |
`” | 双引号 | |
`0 | Null | |
`` | 反引号本身 |
字符 | 描述 | 示例 |
---|---|---|
. |
当前目录 | Ii . #用资源浏览器打开当前目录 |
.. |
父目录 | Cd … #切换到父目录 |
\ |
驱动器根目录 | Cd \ #切换到驱动器的顶级根目录 |
~ |
home,PowerShell初始化的目录 | Cd ~ #切换到PowerShell初始化的目录 |
- 通过变量的$var.getType()方法可以打印变量的数据类型
- 通过$var -is [typename]判断是否是指定类型
- 通过$var -eq $null判断变量是否为null
通过$
符定义变量,例如:
$str='hello'
上述代码中,我们没有给变量指定数据类型,Powershell会给数据分配一个最佳的数据类型,规则如下:
通过$str.getType()
获取$str
的数据类型,输出:
$str.getType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
Type本身也有类型和属性,比如常用的name属性:
$type=$str.getType()
$type.getType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
False True RuntimeType System.Reflection.TypeInfo
在变量前加[typename]
来为变量指定类型,加入类型检查有助于提高程序的鲁棒性:
PS> [byte]$b=101
PS> $b
101
常用类型
typename | 具体类型 |
---|---|
string | String |
array | Array |
byte | Byte |
DateTime | DateTime |
XML | XML |
()+,-,*,/,%,++,--
PS C:\pstest> 1+2+3
6
PS C:\pstest> 0xABCD
43981
PS C:\pstest> 3.14*10*10
314
PS C:\pstest> 1+3-(2.4-5)*(7.899-4.444)
12.983
示例2:流量/容量计算
PS C:\pstest> 80kb*800*30/1gb
1.8310546875
PS C:pstest> 10GB/(80KB*5)/30
873.813333333333
设置数值格式
普通字符串:
# 双引号与单引号都可以定义字符串,但是双引号支持字符串模版,而单引号会忽略所有转义字符
$str = "hello"
$str = 'hello'
Here-String字符串:
# 双引号支持字符串模版
$str = @"
hello
world
"@
# 单引号会忽略所有转义字符,显示原生字符串
$str = @'
hello
world
'@
关于字符串双引号与单引号为什么会有这种区别,可以参考这篇文章:Powershell 执行上下文
示例:对象方法
$url="https://www.pstips.net"
# 以“:”, “.”, “/”为分隔符
$url.split(":./")
# 以“:”, “.”, “/”为分隔符,结果过滤掉空字符串
$url.split(":./",[StringSplitOptions]::RemoveEmptyEntries)
示例:静态方法
function RemoveSpace([string]$text) {
$private:array = $text.Split(" ", `
[StringSplitOptions]::RemoveEmptyEntries)
[string]::Join(" ", $array)
}
格式化操作符 –F 能够将一个字符串格式化为指定格式,左边是包含通配符的字符串,右边是待插入和替换的字符串。
# 单个变量
# -F 右边的表达式必选放在圆括号中,作为一个整体,先进行计算,然后在格式化。否则可能会解析错误
“{0} diskettes per CD” -f (720mb/1.44mb)
500 diskettes per CD
# 多个变量
“{0} {3} at {2}MB fit into one CD at {1}MB” -f (720mb/1.44mb), 720, 1.44, “diskettes”
500 diskettes at 1.44MB fit into one CD at 720MB
通配符主要被使用在文件系统中,在字符串操作符-like
和-notlike
中也可以使用。
通配符 | 描述 | 示例 |
---|---|---|
* |
任意个任意字符,(包含零个字符) | #列出当前目录中的文本文件 Dir *.txt |
? |
一个任意字符 | #列出当前目录中后缀名以‘t’结尾,并且后缀名只有三个字符的文件 Dir *.??t |
[xyz] |
一个包含在指定枚举集合中的字符 | #列出当前目录中以‘a’、‘b’或‘c’打头的文件 Dir [abc]. |
[x-z] |
一个包含在指定区间集合中的字符 | #列出当前目录中包含一个’p’到’z’之间任意字符的文件 Dir [p-z]. |
$ip = Read-Host "IP address"
If ($ip -like "*.*.*.*") { "valid" } Else { "invalid" }
模式匹配不能满足复杂的需求,此时可以使用-match
操作符对数据进行正则匹配。
示例:
# 文本模式包含了6个Tab字符分割的数组
$pattern = "(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)"
# 输入日志
$text = Get-Content $env:windir\windowsupdate.log
# 从日志文件中提取出任意行(这里是第21行)
$text[20] -match $pattern >null
# 每执行一次-match都可以通过$matches获取到最新的匹配结果
if($matches.length>0){
"On {0} this took place: {1}" -f $matches[1], $matches[6]
}
On 2014-02-10 this took place: * Added update {17A5424C-4C70-4BB4-8F83-66DABE5E7CA2}.201 to search result
示例:给正则子表达式取名
# 文本模式包含了6个Tab字符分割的数组
$pattern = "(?.*)\t(? + "\t(?.*)\t(?.*)\t(?.*)"
# 输入日志
$text = Get-Content $env:windir\windowsupdate.log
$text[20] -match $pattern >null
if($matches.length>0){
# 起名后,可以用名称引用
"On {0} this took place: {1}" -f $matches.time, $matches.text
}
On 2014-02-10 this took place: * Added update {17A5424C-4C70-4BB4-8F83-66DABE5E7CA2}.201 to search result
示例:使用switch语句实现上述日志打印
# 为结果创建一个哈希表:
result = @{Defender=0; AutoUpdate=0; SMS=0}
# 解析更新日志,并将结果保存在哈希表中:
Switch -regex -file $env:windir\wu1.log {
'START.*Agent: Install.*Defender' { $result.Defender += 1 };
'START.*Agent: Install.*AutomaticUpdates' { $result.AutoUpdate +=1 };
'START.*Agent: Install.*SMS' { $result.SMS += 1}
}
# 输出结果:
$result
Name Value
---- -----
SMS 0
Defender 1
AutoUpdate 8
字面量方式定义数组:
$arr=(1,1,6,56,55,99,254,0,0,8,67)
# 可以省略()
$arr=1,1,6,56,55,99,254,0,0,8,67
# 定义空数组
$arr=@()
# 定义单元素数组
$arr=, 1
# 多态数组
$arr="hello", 1, (get-date)
# 强类型数组,指定数组类型是int[]
[int[]] $nums=@()
$nums+=2012
# 连续整数数组
$nums = 1..5
新建对象方式定义数组:
# 定义24位byte类型数组
$arr=New-Object Byte[] 24
PS> $books="元素1","元素2","元素3"
# 通过下标访问,下标从0开始
PS> $books[0]
元素1
PS> $books[($book.Count-1)]
元素3
PS> $books[-1]
元素3
# 获取子数组
PS> $subbooks = $books[0,2]
# 将数组逆序输出(其实也是一个子数组)
PS> $books[($books.Count)..0]
元素3
元素2
元素1
# 给数组添加元素
PS> $books+="元素4"
# 给数组删除元素(删除首个元素)
PS> $books=$books[1..($books.Count-1)]
数组属于引用类型,使用默认的的赋值运算符在两个变量之间赋值只是复制了一个引用,两个变量共享同一份数据。此时可以使用clone()方法创建数组的副本:
$chs=@("A","B","C")
$chsBak=$chs
$chsNew=$chs.Clone()
PS> $chs.Equals($chsBak)
True
PS> $chs.Equals($chsNew)
False
#创建二维对称数组
$array=New-Object 'string[,]' 2,2
#给二维对称数组赋值
PS> $array[0,0]="moss"
PS> $array[0,1]="fly"
#访问二维对称数组
PS> $array
moss
fly
PS> $array[0,1]
fly
前面使用@()
创建数组,现在使用@{}
创建哈希表,使用哈希表的键访问对应的值。
数组使用,
作为元素分隔符,哈希表使用;
作为分隔符。
PS> $stu=@{ Name = "小明";Age="12";sex="男" }
PS> $stu
Name Value
---- -----
Name 小明
Age 12
sex 男
# 读操作
PS> $stu["Name"]
小明
PS> $stu["age"]
12
# 哈希表的常用方法
PS> $stu.Count
3
PS> $stu.Keys
Name
Age
sex
PS> $stu.Values
小明
12
男
# 写操作
# 新增字段
$stu.hobby="tennies"
# 删除字段
$stu.Remove("Name")
特殊目录 | 描述 | 示例 |
---|---|---|
Application data | 存储在本地机器上的应用程序数据 | $env:localappdata |
User profile | 用户目录 | $env:userprofile |
Data used incommon | 应用程序公有数据目录 | $env:commonprogramfiles |
Public directory | 所有本地用户的公有目录 | $env:public |
Program directory | 具体应用程序安装的目录 | $env:programfiles |
Roaming Profiles | 漫游用户的应用程序数据 | $env:appdata |
Temporary files(private) | 当前用户的临时目录 | $env:tmp |
Temporary files | 公有临时文件目录 | $env:temp |
Windows directory | Windows系统安装的目录 | $env:windir |
除了上述表格中列出的部分变量,Windows还可以通过下面方法查找全部内置目录:
[System.Environment+SpecialFolder] |
Get-Member -static -memberType Property |
ForEach-Object { "{0,-25}= {1}" -f $_.name, [Environment]::GetFolderPath($_.Name)
}
在cmd中查看环境变量path
只需输入:echo %path%
或者set %path%
,但是在powershell中略有不同,需要输入:
$Env:path
# 当前会话中修改path变量,如果需要全局修改需要对powershell做一些配置
$Env:path=$Env:Path+";D:/test"
powershell中自定义对象对应的类型是:PSCustomObject。我们可以通过以下几种方式来构建自定义对象:
select-object
:把空字符串pipe到该指令,并通过-property
来指定各个属性名称New-Object
+Add-Member
:可以通过新建PSCustomObject对象,再为之添加属性的方式,这种方式最为繁杂,已经不推荐[PSCustomObject][Ordered]@{}
:ordered为可选项,可以直接在哈希表中为对象定义属性和初始化(强烈推荐)示例:
PS> $obj = '' | select-object -property Name, Age, Sex
PS> $obj.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False PSCustomObject System.Object
示例:
PS> $obj = new-object pscustomobject
PS> $obj | add-member -membertype noteproperty -name Param -value 1
PS> $obj
Param
-----
1
示例:
PS> $obj = [pscustomobject]@{Name='haha';Age=2}
PS> $obj
Name Age
---- ---
haha 2
先看一个引例,现在有一个test.ps1,其内容为:
$windows = $env:windir
“Windows Folder: $windows”
然后在控制台依次执行:
PS> $windows="Hellow"
PS> .\test.ps1
Windows Folder: C:\Windows
PS> $windows
Hellow
调用脚本时,会分配一个变量$windows,在脚本调用结束后,这个变量被回收,脚本中的变量不会影响脚本外的变量,因为它们在不同的作用域中。powershell会针对每个函数和脚本给它们分配不同的作用域。
同样是刚才的脚本,刚才的命令,只是在运行脚本时多加上一个点”.” 和一个空格:
PS> $windows="Hellow"
PS> . .\test.ps1
Windows Folder: C:\Windows
PS> $windows
C:Windows
在运行脚本时使用一个原点和空格,Powershell解释器就不会为脚本本身创建自己的变量作用域,它会共享当前控制台的作用域,这种不太灵活但却简单的方法,使用时一定要格外小心。
作用域 | 描述 | 示例 |
---|---|---|
$global |
全局变量,在所有的作用域中有效,如果你在脚本或者函数中设置了全局变量,即使脚本和函数都运行结束,这个变量也任然有效 | 定义: $global:var=“I am global” 访问: $global:var |
$script |
脚本变量,只会在脚本内部有效,包括脚本中的函数,一旦脚本运行结束,这个变量就会被回收 | $script:var=“I am script” |
$private |
私有变量,只会在当前作用域有效,不能贯穿到其他作用域 | $private:var=“I am private” |
$local |
默认变量,可以省略修饰符,在当前作用域有效,其它作用域只对它有只读权限 | $local:var=“I am local” |
打开Powershell控制台后,Powershell会自动生成一个新的全局作用域。如果增加了函数和脚本,或者特殊的定义,才会生成其它作用域。
在当前控制台,只存在一个作用域,通过修饰符访问,其实访问的是同一个变量
PS> $logo="www.pstips.net"
PS> $logo
www.pstips.net
PS> $private:logo
www.pstips.net
PS> $script:logo
www.pstips.net
PS> $private:logo
www.pstips.net
PS> $global:logo
www.pstips.net
当我们把一个命令的执行结果保存到一个变量中,可能会认为变量存放的是纯文本。
但是,事实上Powershell会把文本按每一行作为元素存为数组。如果一个命令的返回值不止一个结果时,Powershell也会自动把结果存储为数组。
(expression)
(100+8) * 7
(Get-Process -ProcessName "WeChat").CPU
$(expression)
# 单个值
PS /> $str1 = "123"
PS /> $($str1)
123
PS /> $($str1)[0]
1
# 数组类型
PS /> $str2 = "123","456","789"
PS /> $($str2)
123
456
789
PS /> $($str2)[0]
123
@(expression)
PS /> $str1 = "123"
PS /> @($str1)
123
PS /> @($str1)[0]
123
语句格式:if (condition1) { block } else if(condition1) { block } else { block }
示例:数字
# 使用 Switch
switch($value){
1 {"Beijing"}
2 {"Shanghai"}
3 {"Tianjin"}
4 {"Chongqing"}
Default {"没有匹配条件"}
}
示例:取值范围
$value=2
# 多条件匹配,处理多次
switch($value){
{$_ -lt 5 } { "小于5" }
{$_ -gt 0 } { "大于0" }
{$_ -lt 100}{ "小于100"}
Default {"没有匹配条件"}
}
小于5
大于0
小于100
# 多条件匹配,处理1次
switch($value){
{$_ -lt 5 } { "小于5"; break }
{$_ -gt 0 } { "大于0"; break }
{$_ -lt 100}{ "小于100"; break }
Default {"没有匹配条件"; break }
}
示例:字符串
$domain="www.mossfly.com"
# 字符串默认使用-eq比较,大小写不敏感
switch($domain){
"Www.moSSfly.com" {"Ok 1"}
"www.MOSSFLY.com" {"Ok 2" }
"WWW.mossfly.COM" {"Ok 3"}
}
Ok 1
Ok 2
Ok 3
# 大小写敏感
switch -case ($domain){
"Www.moSSfly.com" {"Ok 1"}
"www.MOSSFLY.com" {"Ok 2" }
"www.mossfly.com" {"Ok 3"}
}
Ok 3
# 使用通配符
switch -wildcard($domain){
"*" {"匹配'*'"}
"*.com" {"匹配*.com" }
"*.*.*" {"匹配*.*.*"}
}
匹配'*'
匹配*.com
匹配*.*.*
#使用正则
switch -regex ($mail){
"^www" {"www打头"}
"com$" {"com结尾" }
"d{1,3}.d{1,3}.d{1,3}.d{1,3}" {"IP地址"}
}
www打头
com结尾
示例:处理集合
$value=100..999
switch($value){
# 数组元素使用$_去获取
{[Math]::Pow($_%10,3)+[Math]::Pow( [Math]::Truncate($_%100/10) ,3)+[Math]::Pow( [Math]::Truncate($_/100) , 3) -eq $_} {$_}
}
153
370
371
407
- for语句格式:for ($i=1; $i -lt 100; $i++) { block }
- foreach语句格式:foreach($var in $list){ block }
- do-while语句参考:Powershell Do While 循环
示例1:foreach打印数组元素
$strs = @"
a
b
c
"@
$strlist = ($strs -split "\n")
foreach($s in $strlist) {
$s+"-suffix"
}
示例2:for循环实现进度条
for ($i=1; $i -lt 100; $i++) {
write-progress -activity "starting..." -percentcomplete $i -currentoperation "$i Finished"` -status "loading..."
start-sleep 1
}
for ($p=100; $p -ge 0; $p--) {
Write-Progress -Activity "Starting..." -SecondsRemaining $p -CurrentOperation $p% Not Finished" -Status "Loding..."
Start-Sleep 1
}
示例4:for循环实现输入校验
for($domain="";!($domain -like "www.*.*");$domain=Read-Host "Input domain")
{
Write-Host -ForegroundColor "Green" "Please give a valid domain name."
}
Please give a valid domain name.
Input domain: www
Please give a valid domain name.
Input domain: mossfly.com
Please give a valid domain name.
示例5:For循环的控制语句第一个和第三个可以为空
$sum=0
$i=1
for(;$i -le 100;) {
$sum+=$i
$i++
}
$sum
无参函数:
# 定义函数
function letitrun {
代码逻辑块
}
# 调用函数
letitrun
有参函数:
# 定义函数
function letitrun($data,$data2){
代码逻辑块引用$data,$data2
}
# 调用函数
letitrun "abc" 2
# 或者指定参数名
letitrun -data "abc" -data2 2
有参函数带默认值:
# 定义函数,这里[switch]参数就是[bool]参数,data3这个参数做了参数校验
function letitrun([string]$data="hello",[int]$data2=10,[bool] $data3=$(throw "Parameter missing: -name data3"),[switch]$data4=true){
代码逻辑块引用$data,$data2,$data3,$data4
}
# 调用函数
letitrun "abc" 2 true false
# 或者不传参数
letitrun
命名参数可以进行参数校验,参数校验的方式除了抛出异常外,还可以通过对话框的方式提示用户或让用户选择,参考这个例子:Powershell使用Dialog设定必选参数
使用Param关键词定义入数列表:
# 定义函数
function letitrun{
param($data,$data2)
代码逻辑块引用$data,$data2
}
使用可变参数$args
,增加了灵活性,但是不能使用命名参数了:
function sayHello{
# 你可以把$args当做是长度从0到n的数组
if($args.Count -eq 0) {
"No argument!"
} else {
$args | foreach {"Hello,$($_)"}
}
}
# 调用无参
sayHello
# 调用单个参数
sayHello LiLi
# 调用多个参数,参数之间用空格分隔
sayHello LiLi Lucy Tom
函数也可以配合管道使用,但是需要用内建关键词$input
或者$_
去接收,详见下节。
函数返回值:
write-host
或者Write-Debug
进行打印,推荐使用后者,通过$DebugPreference="Continue"
开启调试模式,$DebugPreference="SilentlyContinue"
关闭调试模式。begin
:这个关键字后的语句列表块只会运行一次,而且它必须位于函数的开头定义
process
:这个关键字后的语句列表块会针对管道传递过来的每个对象运行一次,并且会自动赋予$_变量
end
:在处理完所有的对象以后,才会运行一次End关键字后的语句列表块
如果函数体内没有使用任何以上三种功能关键字,那么函数体默认将管道传递来的对象视为End方式。
使用管道传参的函数无法使用参数列表去接收,是必须使用内建关键词$input
或者$_
去接收。
示例:
function myfun{
end{"end: $input"}
}
# 测试
"a","b","c" | myfun
# 输出:
# end: a b c
示例2:
function myfun2{
end {"end $_"}
}
# 测试
"a","b","c" | myfun2
# 输出
# end:
# 测试
"a" | myfun2
# 输出
# end:
# 结论:$_无法获取到管道输入
示例3:
function myfun3{
begin {"begin $_"}
process {"process $_"}
end {"end $_"}
}
# 测试
"a","b","c" | myfun3
# 输出:
# begin
# process a
# process b
# process c
# end c
引例:用WMI去查询系统信息版本号
Get-WmiObject -Class Win32_OperatingSystem -Property Version
可以简写为:
Get-WmiObject Win32_OperatingSystem Version
上述简写执行成功因为实参类型与函数定义是一一对应的,但是如果把实参Win32_OperatingSystem
和 Version
的顺序对调一下,就会报错。为此可以通过Splatting传参来解决实参与形参的对应问题,而不必严格按照函数定义的顺序进行调用:
$Param = @{Class = "Win32_OperatingSystem";
Property = "Version";
ComputerName = "LocalHost"}
Get-WmiObject @Param
splatting其实就是一个集合,其基本格式为:$variable=@{参数名=参数值;参数名=参数值;...}
,然后使用@
符号进行引用。其退化格式为:$variable="参数值", "参数值", ...
,不过参数的顺序就必须与函数定义顺序一致了,比如上述的语句可以改写为:
$Param = "Win32_OperatingSystem", "Version"
Get-WmiObject @Param
除了解除入参顺序问题,splatting的另一个重要意义是实现参数复用,比如:
$Colors = @{ForegroundColor = "yellow";
BackgroundColor = "red"}
Write-Host "I love PowerShell" @Colors
Write-Host "He loves PowerShell, too." @Colors
cmdlet指令可以通过设置-ErrorAction action
来设置本条指令的异常处理策略,公有以下几种策略:
策略名称 | 描述 |
---|---|
Stop |
发生异常时抛出异常信息,并中断执行 |
Continue |
发生异常时抛出异常信息,并继续执行 |
SilentlyContinue |
发生异常时屏蔽异常信息,并继续执行 |
由于系统默认的ErrorActionPreference为continue,因此哪怕指定了指令的-ErrorAction Stop
也不会阻止整个脚本继续执行。但是可以在trap语句块中使用Break来终止脚本继续运行。
异常相关的其他知识点:
-ErrorAction "SilentlyContinue"
抑制异常中断,-ErrorAction
可以简写为-ea
;-ErrorVariable var
将异常保存到一个变量中,var是一个数组;$?
获取异常状态:发生异常该值为false,否则为true;$error
获取错误堆栈,最新的异常保存在索引为0的位置,所以一般不需要为指令设置-ErrorVariable var
。$error
的元素实体为System.Management.Automation.ErrorRecord
,如果要获取异常,需要通过$($error[0]).Exception.Message
属性;Remove-Item "文件不存在" -ErrorAction "SilentlyContinue"
If (!$?){
"发生异常,异常信息为$($error[0])"
} else {
"删除文件成功!"
}
# 发生异常,异常信息为:找不到路径“E:文件不存在”,因为该路径不存在。
trap
获取异常信息trap{}
语句块相当于其他语言的catch语句块,只不过trap语句块写在代码的最前面
示例:脚本多处发生异常,可以执行完,且trap只捕获到第一次异常,如果想要trap捕获每个异常,则应为每个指令设置-ErrorAction Stop
Trap {
# 在trap语句块中,可以通过$_获取异常信息
Write-Host $_.Exception.Message
};
Stop-Process -Name "NoSuchProcess"
Write-Host "执行下一句"
1/$null
Write-Host "执行结束"
示例:在trap中使用break使脚本在第一次捕获异常时就停止继续执行
Trap {
Write-Host $_.Exception.Message;
Break
};
Stop-Process -Name "NoSuchProcess" -ea Stop
Write-Host "执行下一句"
1/$null
Write-Host "执行结束"
示例:在trap中使用continue屏蔽默认的异常输出,定制自己的异常信息
Function Test-Func {
Trap {
"Trap到了异常: $($_.Exception.Message)";
Continue
}
1/$null
Get-Process "NoSuchProcess" -ErrorAction Stop
Dir MossFly: -ErrorAction Stop
}
Test-Func
Trap到了异常: 试图除以零。
Trap到了异常: 找不到名为“NoSuchProcess”的进程。请验证该进程名称,然后再次调用 cmdlet。
Trap到了异常: 找不到驱动器。名为“MossFly”的驱动器不存在。
trap
处理特定的异常Trap [System.DivideByZeroException] {
"除数为空!";
Continue
}
Trap [System.Management.Automation.ParameterBindingException] {
"参数不正确!";
Continue
}
Trap [System.Net.WebException]{
"网络异常!"
Continue
}
1/$null
Dir -MacGuffin
$wc = new-object System.Net.WebClient
$wc.DownloadFile("http://www.mossfly.com/powershell.txt","e:ps.txt")
#除数为空!
#参数不正确!
#网络异常!
Function Func-Test($a,$b){
if($b -eq $null){
throw "参数b 不能为空!"
}
"{0}+{1}={2}" -f $a,$b,($a+$b)
}
Func-Test -a 10
上述代码可以优化:
Function Func-Test($a,$b=$(throw "参数B 不能为空!")){
"{0}+{1}={2}" -f $a,$b,($a+$b)
}
Func-Test -a 10
我们希望将脚本分成两类,以最大程度实现函数复用:
这在cmd中是无法实现的(cmd以bat文件为单位,无法做到类库的函数级复用),我们来看看powershell中如何实现:
假设有一个类库pslib.ps1:
# 返回输入数字的阶乘
Function Factorial([int]$n){
$total=1
for($i=1;$i -le $n;$i++) {
$total*=$i
}
return $total
}
我们的工作脚本myscript.ps1:
# 工作脚本接收一个外部一个整数入参,否则抛出异常
param([int]$n=$(throw "请输入一个正整数"))
# 导入类库,其实就是执行了该脚本,并利用“. ”提升了类库脚本的作用域,使其执行后没有被立即销毁
. .pslib.ps1
# 调用类库中的函数
Factorial $n
执行工作脚本:
\.myscript.ps1 10
# 在本例中我们使用param()获取入参,可以使用命名参数方式调用脚本
\.myscript.ps1 -n 10
还有一个问题:目前工作脚本需要与类库放在同一个目录才能方便在工作脚本中导入类库脚本,有什么办法解决呢?
我们可以把所有的类库都放在一个用户目录中,比如:$env:appdata
,然后在工作脚本中通过绝对路径引入。具体操作是:把所有的类库放到一个目录下,然后写一个install.ps1,其内容是把当前目录下的所有脚本复制到$env:appdata目录下。然后在任意工作脚本中就可以通过. $env:appdata\xxx.ps1
来导入类库。
技巧:我们可以为工作脚本设置别名来简化调用方式:
set-alias ms .myscript.ps1
ms 10
所有命令参见:Powershell 命令集 cmdlets
示例:接收键盘输入
# 从键盘接收输入数据
$str=read-host -prompt "Type something:"
# 从键盘接收安全字符(显示为*号)
$sstr=read-host -assecurestring
# 把密文恢复为原始输入的明文
$sstr=[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sstr)
$str=[System.Runtime.InteropServices.Marshal]::PtrToStringAuto($sstr)
# 向命令行打印字符串(等同于直接输出$str或者echo $str)
write-host $str
$str="hello powershell"
$strBytes=[System.Text.Encoding]::Unicode.GetBytes($str)
# 发现[System.Convert]可以简化为[Convert]
$encodedStr=[Convert]::ToBase64String($strBytes)
示例:使用默认加密方式加解密
$password="abc"
# 没有指定加密方式,默认使用原生的DPAPI(Windows Data Protection API)算法
$securePassword=$password | ConvertTo-SecureString -AsPlainText -Force
$standardPassword=ConvertFrom-SecureString $securePassword
示例:使用AES加密算法
$password="abc"
# 定义长度为24或36的AES的常量key
# $key=(3,4,5,66,77,254,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7)
# 随机生成长度为24的AES的key
$key=New-Object Byte[] 24
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($key)
$spassword=$password | ConvertTo-SecureString -AsPlainText -Force
$stdpassword=ConvertFrom-SecureString $spassword -Key $key
此命令应用于字符串数组,对应于cmd的findstr
命令,示例:
ipconfig | Select-String "IP"
示例:使用New-Object新建PSObject对象并添加属性
PS C:Powershell> $obj=New-Object PSObject
PS C:Powershell> Add-Member -Name A -Value 1 -InputObject $obj
# NoteProperty属性为静态数据,用常量赋值
# 与之对应的ScriptProperty则是动态属性,用{}表达式赋值,可以做到动态返回值
PS C:Powershell> Add-Member -MemberType NoteProperty -Name "A" -Value "1" -InputObject $obj
PS C:Powershell> Add-Member -MemberType ScriptProperty -Name "B" -Value {get-date} -InputObject $obj
PS C:Powershell> Add-Member -MemberType NoteProperty -Name "C" -Value "3" -InputObject $obj
PS C:Powershell> Add-Member -MemberType NoteProperty -Name "D" -Value "4" -InputObject $obj
PS C:Powershell> $obj
A B C D
- - - -
1 2 3 4
MemberType表格
类型 | 描述 |
---|---|
AliasProperty | 另外一个属性的别名 |
CodeProperty | 通过静态的.Net方法返回属性的内容 |
Property | 真正的属性 |
NoteProperty | 随后增加的属性 |
ScriptProperty | 通过脚本执行返回一个属性的值 |
ParameterizedProperty | 需要传递参数的属性 |
示例 | 使用New-Object新建PSCredential对象 |
示例:创建PSCredential对象
$credentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username $password
示例:使用New-Variable创建变量,且为常量
New-Variable a -value 1 -option Constant
$a
此命令需配合管道符,一般应用于对象数组。
Where-Object
:类似于stream中的filter操作符,不会改变元素数据类型Sort-Object
:可以对数据进行排序,不会改变元素数据类型ForEach-Object
:类似于stream中的map操作符,可以实现元素类型转换Select-Object
:对过滤结果的属性、数量进行筛选,不改变元素类型。比如结合-first count参数,可以实现结果立即返回,这在递归查找已知数量的文件的指令中可以大大提高运行速度示例:Where-Object基本用法
Get-Process | Where-Object{$_.ProcessName -eq "svchost"}
# 等价于
Get-Process | Where-Object{$PSItem.ProcessName -eq "svchost"}
# 等价于
Get-Process | Where-Object ProcessName -eq "svchost"
示例:综合演示利用管道筛选对结构化数据进行聚合统计
现有结构化数据a.txt:
李一 93
王二 83
王三 93
李四 60
王五 75
马六 61
孙七 75
刘八 75
实现打印出成绩相同的学生及成绩
Get-Content .\a.txt | ForEach-Object {
[PSCustomObject]@{
Name = $_.split()[0]
Value = $_.split()[1]
}
} | Group-Object Value | Where-Object { $_.Count -gt 1 }|
ForEach-Object { $_.Group | ForEach-Object { "{0} {1}" -f $_.name,$_.value } }
示例:利用Select-Object对结果的属性进行筛选
Dir | Select-Object name, length, LastWriteTime |
ConvertTo-HTML | Out-File report.htm
Format-List
命令可以把对象信息以列表的形式展现出来。Format-Table
命令可以把对象信息以表单的形式展现,同时还支持配合一些特定参数来动态调整表单样式。Format-Wide
Format-Custom
示例:基础用法
# 查看对象结果的所有属性
ls | Format-Table *
# 使用文本换行参数处理属性和属性的内容太多可能不会显示完全
ls | Format-Table * -Wrap
# 优化列宽度:将属性值的最大宽带作为每一列的宽度
ls | Format-Table -AutoSize
# 显示指定的属性
ls | Format-Table Name,Length,LastWriteTime
# 使用通配符(显示以pe打头,64结尾的文件)
ls | Format-Table Name,pe*64
示例:自定义表格列标题
Get-Process | Where-Object{$_.Id -gt 2000} | Format-Table ProcessName, @{Expression={$_.Id}; Name="Id>2000";FormatString="N2"}
示例1:获得所有名为svchost的进程信息
Get-Process | Where-Object{$_.ProcessName -eq "svchost"}
示例2:在已经罗列出来的svchost进程信息里继续筛选出Id属性值大于1000的
Get-Process | Where-Object{$_.ProcessName -eq "svchost"} | Where-Object{$_.Id -gt 1000}
示例3:查看所有当前系统里的进程,但条件是进程的Id号大于1000以内的前十个进程信息名,并且Id号需要从大到小排列
Get-Process | Where-Object {$_.Id -gt 1000} | Select-Object -Property Id, ProcessName -First 10 | Sort-Object -Property Id -Descending
示例4:将示例3的结果以表格形式呈现
Get-Process | Where-Object {$_.Id -gt 1000} | Select-Object -Property Id, ProcessName -First 10 | Sort-Object -Property Id -Descending | Format-Table -AutoSize
示例5:获取进程并将之杀掉
(Get-Process -ProcessName "Thunder").Kill()
格式:Stop-Process -Name pname
Function ErrorTest(){
#从这里开始隐藏所有的错误信息
$ErrorActionPreference="SilentlyContinue"
#关闭一个不存在的进程,默认会报错,在这里错误信息被隐藏
Stop-Process -Name "www.mossfly.com"
#恢复$ErrorActionPreference,错误开始输出
$ErrorActionPreference="Continue"
# 整数除以0,报错,错误信息正常输出
2/0
}
ErrorTest
试图除以零。
命令 | 描述 | 别名 | 示例 |
---|---|---|---|
Get-Childitem | 列出目录的内容,返回的是FileInfo数组 | Dir, ls, gci | |
Get-Content | 基于文本行来读取内容,返回的是个字符串数组 | type, cat, gc | |
Set-Content | 覆盖写入文件内容 | sc | Set-Content info.txt "First line" |
Add-Content | 追加写入文件内容,在涉及到文本操作的时候建议使用Add-Content替代>> ,因为后者默认使用的是控制台的字符集,容易乱码 |
Add-Content info.txt "Third line" |
|
Get-Item | 获取指定的文件或者目录 | gi | |
Get-ItemProperty | 获取文件或目录的属性 | gp | |
Set-ItemProperty | 设置文件或路径的属性 | sp | |
Get-Location | 获取当前路径 | pwd | |
Set-Location | 更改当前目录的位置 | Cd,chdir, sl | # 从环境变量中获取系统目录 (绝对路径)Cd $env:windir |
Push-Location | 切换到新目录,并保存当前目录 | pushd | |
Pop-Location | 切回上个目录 | popd | |
Invoke-Item | 使用对应的默认windows程序运行文件或者目录 | ii | |
Join-Path | 连接两个路径为一个路径 | – | |
Copy-Item | 复制文件或者目录 | cp, cpi | Copy-Item -recurse $home\*.ps1 ([Environment]::GetFolderPath("Desktop")) |
Move-Item | 移动文件或者目录 | mi, mv, move | Move-Item ($desktop + "\*.ps1") ($desktop + "\PS Scripts") |
New-Item | 创建新文件或者目录 | ni md和mkdir相当于ni -type directory |
$file = New-Item testfile.txt -type file |
Remove-Item | 删除空目录或者文件 | ri, rm, rmdir,del, erase, rd | del testfile.txt |
Rename-Item | 重命名文件或者路径 | rni, ren | |
Resolve-Path | 处理相对路径或者包含通配符的路径,返回的是PathInfo数组,注意与gci的区别 | rvpa | 相对路径转换成绝对路径 Resolve-Path .\a.png |
Split-Path | 提取路径的特定部分,例如父目录,驱动器,文件名 | – | |
Test-Path | 测试指定的路径是否存在 | – | |
New-PSDrive | 创建一个新的驱动器(本地或网络都可) | – | New-PSDrive -name network -psProvider FileSystem -root \\127.0.0.1\share |
Remove-PSDrive | 删除创建的驱动器,如果该驱动器正在使用则不能删除 | – | Remove-PSDrive network |
像文件查找、删除等操作都支持-recursive参数
示例:基本使用
Set-Content -Value $content -Path D:\tmp.txt
$cache = Get-Content -Path D:\tmp.txt
Get-ChildItem基本用法:
# 不指定目录则是当前目录
# 仅列出子节点,非递归
Get-ChildItem C:\Windows
示例:获得C:\Windows目录下所有大小超过200 bytes的文件
Get-ChildItem C:\Windows | Where-Object -FilterScript {$_.Length -gt 200}
示例:Resolve-Path查找指定文件
function edit-file([string]$path=$(Throw "请输入相对路径!")) {
# 处理相对路径,并抑制错误
$files = Resolve-Path $path -ea SilentlyContinue
# 验证是否有错误产生:
if (!$?){
# 如果是,没有找到符合标准的文件,给出提醒并停止:
"没有找到符合标准的文件.";
break
}
# 如果返回结果为数组,表示有多个文件:
if ($files -is [array]){
# 此种情况下,列出你想打开的文件:
Write-Host -foregroundColor "Red" -backgroundColor "White" `
"你想打开这些文件吗?"
foreach ($file in $files){
"- " + $file.Path
}
# 然后确认这些文件是否为用户想打开的:
$yes = ([System.Management.Automation.Host.ChoiceDescription]"&yes")
$no = ([System.Management.Automation.Host.ChoiceDescription]"&no")
$choices = [System.Management.Automation.Host.ChoiceDescription[]]($yes,$no)
$result = $host.ui.PromptForChoice('Open files','Open these files?',$choices,1)
# 如果用户确认,使用"&"操作符启动所有的文件
if ($result -eq 0){
foreach ($file in $files){ & $file }
}
} else {
# 如果是单个文件,可以直接使用"&"启动:
& $files
}
}
# 新建一个文件夹
New-Item test/Folder -ItemType directory
# 新建一个文本文件并写数据
New-Item test/Folder/File.txt -ItemType file -Value testcontent
示例:删除创建时间最早的文件夹及其子文件
$Url="F:/123"
$File=Get-ChildItem $Url -Recurse |Where-Object {$_.PsIsContainer -eq $true}|sort CreationTime |select FullName -First 1
$Url2=$File.FullName
Remove-Item $Url2'/*'
Remove-Item $Url2
Write-Host -ForegroundColor Red -BackgroundColor Yellow $File.FullName 文件已经被删除。
别名: cp、cpi
# splatting写法
$ArrayArguments = "C:\PowerShell.txt", "C:\test\"
Copy-Item @ArrayArguments
# 等同于
Copy-Item -Path "C:\test1.txt" -Destination "D:\"
参考:PowerShell文件系统(五)管理访问权限
$data = Import-Csv C:\ProcessInfo.csv
$data | Get-Member
假设有test.xml,内容如下:
<office>
<staff branch="Hanover" Type="sales">
<employee>
<Name>Tobias WeltnerName>
<function>managementfunction>
<age>39age>
employee>
<employee>
<Name>Cofi HeideckeName>
<function>securityfunction>
<age>4age>
employee>
staff>
<staff branch="London" Type="Technology">
<employee>
<Name>XXXXName>
<function>gementfunction>
<age>39age>
employee>
staff>
office>
$xml=new-object XML
# load方法传入xml文件的相对或绝对路径
$xml.load('.\test.xml')
# 至此,test.xml已经导入完成,我们打印一下
$xml
# 输出xml为纯文本
$xml.get_InnerXml()
也可以直接通过强转的方式:
[XML]$xml=(Get-Content .\test.xml)
# 或者
$xml = [XML](Get-Content .\test.xml)
上面代码中$xml是一个XML对象,对这个对象的修改操作只是内存对象发生改变不会同步到xml文件,可以通过下节方法使修改保存到文件
$xml.Save(“$env:temp\updateddata.xml”)
示例:通过属性名访问
# 也可以这样直接获取,注意这里因为有多个节点所以返回的是数组
$staff = $xml.office.staff
# 读取staff的branch属性
foreach($s in $staff){ $s.branch }
# 如果访问的是office,就不是一个数组,而是一个节点对象了
$office = $xml.office
示例:通过SelectNodes()来访问——XPath方式
# 这里拿到的是一个数组,因此可以用foreach遍历
$staff = $xml.selectNodes('/office/staff')
# XPath支持在方括号中使用通配符访问,这里只会返回第一个员工结点
$staff1 = $xml.selectNodes('/office/staff[1]')
# 返回年龄小于18岁的员工列表
$staff = $xml.selectNodes('/office/staff[age<18]')
# 返回最后一位员工信息
$staff = $xml.selectNodes('/office/staff[last()]')
# 返回第3位及以后的员工信息
$staff = $xml.selectNodes('/office/staff[position()>1]')
示例:添加新结点
# 创建新的结点:
$newemployee = $xml.CreateElement("employee")
$newemployee.set_InnerXML( `
"Bernd Seiler expert ")
# 插入新结点:
$xml.staff.AppendChild($newemployee)
# 验证结果:
$xml.staff.employee
参考:PowerShell处理XML(二)加载和处理XML文件](https://www.pstips.net/loading-and-processing-xml-files.html)
# 获取所有属性
$xmldata.staff.get_Attributes()
# 获取指定属性
$xmldata.staff.GetAttribute("branch")
# 指定新的属性,或者更新(重写)已有的属性
$xmldata.staff.SetAttribute("branch", "New York")
ConvertTo-HTML
我们可以使用Framework中的WebClient下载文件,但是在PowerShell 3.0 中直接提供了Invoke-WebRequest
:
$src = 'https://www.pstips.net/index.php'
$des = "$env:temp\index.php"
Invoke-WebRequest -uri $src -OutFile $des
Unblock-File $des
Function Detect-OuterWebLinks ([string]$website){
$siteHost=([Uri]($website)).Host
$site = Invoke-WebRequest -UseBasicParsing -Uri $website
$site.Links | where { ([uri]($_.href)).Host -ne $siteHost } | select -ExpandProperty outerHTML
}
上一节中我们使用New-Object命令可以创建对象,这些对象包括.NET Framework以及COM object。
.NET Framework
是Windows类框架库,PowerShell底层就是构建于此,相当于Windows SDK;COM
全称是component,一套微软定义的与语言、平台无关的组件化架构;# 创建COM对象
$app = new-object -comobject "shell.application"
# 创建.NET对象
$date = new-object -typename system.datetime 2021, 4, 2
本节主要介绍.NET Framework的一些常用类。
- 对于陌生的类,可以到微软官网查询其构造方法及属性、方法
- 完整类型通常以System开头,而实际使用中允许省略System,例如[System.DateTime]可以简写为[DateTime]
# 获取当前日期
$now = get-date
# 新建日期对象
$dt = new-object -typename system.datetime -argumentlist 2020, 4,2,12,58,59
$rand = New-Object system.random
$rand.next(1,50)
$guid = [GUID]::NewGUID()
Foreach ($format in "N","D","B","P") { "GUID with $format : {0}" -f $GUID.ToString($format)}
GUID with N : e1a5d98f4227470b84c2b37a6a8fb894
GUID with D : e1a5d98f-4227-470b-84c2-b37a6a8fb894
GUID with B : {e1a5d98f-4227-470b-84c2-b37a6a8fb894}
GUID with P : (e1a5d98f-4227-470b-84c2-b37a6a8fb894)
$Port = 8888
$Url = ""
$listener = New-Object System.Net.HttpListener
$prefix = "http://*:$Port/$Url"
$listener.Prefixes.Add($prefix)
$listener.Start()
使用 $listener.GetContext() 或 $listener.BeginGetContext 都是等待任务运行完成才能处理下一个请求,如何同时处理多个请求?
二者是微软推出的新的网络请求客户端实现,用于弥补System.Net.Client的一些缺陷。
$r = [System.Net.WebRequest]::Create("https://www.pstips.net/")
$startTime = Get-Date
$resp = $r.GetResponse()
$stopTime = Get-Date
$resp.close()
$ResponseTime = ($stopTime - $startTime).TotalMilliseconds
$ResponseTime
包含帐号、密码两个字段,用于登录凭据的本地加密存储。
示例:
$password="password"
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "Administrator" $password
$pwd=(Get-Credential -Credential 'Administrator').password
示例:逐行读取文件
for($file=[IO.File]::OpenText("c:autoexec.bat") ; !$file.EndOfStream);$line=$file.ReadLine() ){
$line;
}
$file.Close()
path = Join-Path ([Environment]::GetFolderPath("Desktop")) "test.txt"
$path
C:\Users\mosser\Desktop\test.txt
$path = [System.IO.Path]::Combine([Environment]::GetFolderPath("Desktop"), "test.txt")
$path
C:\Users\mosser\Desktop\test.txt
所谓系统接口是指powershell中Windows平台专属接口,这部分接口在core 6.0中仅在Windows平台有效,因此有必要做一个梳理。
window注册表:PowerShell注册表(一)操作注册表的几条重要命令
关于Windows注册表:注册表
假如有Math2.dll,其中有函数:
namespace Math {
public class Methods {
public Methods() {
}
public static int CompareI(int a, int b) {
if (a>b)
return a;
else
return b;
}
public int CompareII(int a, int b) {
if (a>b)
return a;
else
return b;
}
}
}
# 加载dll库
[void][reflection.assembly]::LoadFile("G:/Math2.dll")
# 调用静态方法
[Math.methods]::CompareI(10,2)
# 调用非静态方法
$a=New-Object Math.Methods
$a.CompareII(2,3)
因为VbScript是基于COM的,而Powershell是基于.NET的,所以Powershell可以使用New-Object来建立一个VBScript对象。
示例:
# ScriptControl对象只在32位系统可用可用,具有局限性
$sc = New-Object -ComObject ScriptControl
$sc.Language = "VBScript"
$sc.AddCode('
Set obj = CreateObject("WScript.Shell")
obj.popup "VBScript Form",,"hello!",0
')
改进:
# 改为用WScript.Shell就可以了
$vbs = New-Object -ComObject WScript.Shell
$vbs.popup("Popup from PowerShell",$null,"PowerShell in Practice",0)
示例1:打开notepad
notepad.exe .\test.txt
示例2:打开Powershell_ise
# 默认启动
powershell_ise.exe
# 启动并打开多个脚本文件
powershell_ise.exe ”c:\a.ps1,c:\b.ps1”
# 不加载用户的配置文件
powershell_ise.exe –file ”c:\a.ps1” –NoProfile
# 在MTA模式下不加载用户配置
powershell_ise.exe -MTA –NoProfile
# 显示帮助信息
powershell_ise.exe -Help
以Mac为例,使用homebrew安装PowerShell Core:
# 先安装Homebre-Cask
brew tap caskroom/cask
# 直接开始安装PowerShell
brew cask install powershell
# PowerShell的升级方法
brew update
brew cask reinstall powershell
Powershell使用哈希表