文章目录
介绍
PowerShell
作为Windows
系统中的默认脚本,其实在开发中,我们用的很少。接下来的几篇文章都将PowerShell
的相关知识,在上一章中我们介绍了PowerShell
的函数和脚本的相关知识,本章我们将介绍其他相关说明知识,如输入输出、正则表达式的使用等等。
输入输出
PowerShell
的Read-Host
Cmdlet
命令的功能是展示一个文本提示,收集来自用户的输入信息。
Read-Host "Enter a computer name"
Enter a computer name:SERVER-R2
# 输出
# SERVER-R2
该示例突出了Cmdlet
的两个重要事实。
- 提示信息的最后添加了冒号
- 用户键入的任何信息都会作为该
Cmdlet
的返回结果
既然你可以收集输入信息,那么也会希望了解一些展示返回结果的方法。Write-Host
命令就是其中的一种方法。它会和其他Cmdlet
一样使用管道,但是它并不会放任何数据到管道中。相反,它会直接写到宿主应用程序的界面。所以,我们开以使用该命令的-ForegroundColor
和-BackgroundColor
参数来将前景和背景设置为其他颜色。
Write-Host "COLORFUL!" -Fore Yellow -Back Magenta
在针对脚本或者命令来产生常规输出结果而言,这个不是一个好的方法。而对于手动格式化的输出,更不是一个好的选择。再比如,针对产生错误信息、警告信息、调试信息等而言,这也不是一个好的方法,只有当需要展示一个特定的信息,比如使用其他颜色来吸引人们的注意力。
与Write-Host
命令不同,Write-Output
命令可以将对象发送给管道。因为它不会直接写到显示界面,所以也不允许你指定颜色等。
Write-Output "Hello" | Where-Object { $_.Lenght -GT 10 }
Write-Host "Hello" | Where-Object { $_.Lenght -GT 10 }
上面的示例的第一个命令,你不会看到任何结果,因为通过Where-Object
的筛选,并没有可输出的内容。而第二命令会直接输出“Hello
”。Write-Output
命令是PowerShell
默认使用的Cmdlet
命令。
PowerShell
针对每种输出方法都有对应的内置配置变量。如果配置变量设置为“Continue
”,那么我们即将展示给你的命令就会真正产生输出结果。如果配置变量被设置为“SilentlyContinue
”,那么关联的输出命令不会产生任何信息。
Cmdlet | 作用 | 配置变量 |
---|---|---|
Write-Warning | 显示警告信息,默认会以黄色字体显示,同时前面带有“警告:”字样 | $WarningPreference(默认Conitinue) |
Write-Verbose | 显示详细信息,默认会以黄色字体显示,同时前面带有“详细信息:”字样 | $VerbosePreference(默认SilentlyConitinue) |
Write-Debug | 显示调试信息,默认会以黄色字体显示,同时前面带有“调试:”字样 | $DebugPreference(默认SilentlyConitinue) |
Write-Error | 产生一个错误信息 | $ErroActionPreference(默认Conitinue) |
注意:PowerShell v5中添加了一个新的命令:Write-Information
Write-Error
命令会有点不同,因为它会将错误信息写入PowerShell
的错误流中。当然,也可以使用try-catch块来处理出现的错误。
使用正则表达式解析文本文件
正则表达式入门
最简单的正则表达式就是你所期望匹配的文本字符串。PowerShell
默认的匹配规则默认不区分大小写。某些特定的字符在正则表达式中有特殊的含义,这些特殊字符可以允许你检查文本变量中的文本模式。下面是一些例子。
\w
用于匹配“文本字符”,也就是字母、数字以及下划线,但不含标点符号和空格。正则表达式 “\won
” 可以匹配"Don
"、“Ron
"以及"ton
”,\w
可以代表任意字母、数字或下划线\W
和\w
相反,意思是它将会匹配空格于标点符号——也就是“非字母”\d
用于匹配包括0
到9
的任意数字\D
用于匹配任意非数字\s
用于匹配任意空格字符,比如Tab
、空格或者回车等\S
用于匹配任意非空格字符.
(句号)代表任意单个字符[abcde]
用于匹配在该集合中任意字符。正则表达式d[aeiou]n
可以匹配"Don
"、“Dan
”,但是不会匹配"Doun
"或"Deen
"[a-z]
匹配在此范围内的一个或多个字符,可以使用逗号分隔列表指定多个范围,比如[a-f,m-z]
[^abcde]
用于匹配不在该集合中的一个或多个字符,意味着正则表达式d[^aeiou]
可以与"dns
"匹配,但无法与"don
"匹配- 将
?
置于另一个字符或特殊符号后,可以用于匹配该字符的一个实例。所以正则表达式"do?n
"可以匹配"don
",但不会匹配"doon
"。该正则表达式还可以匹配"dn
",这是由于?
还可以代表空实例 *
用于匹配该符号之前任意数量的实例。正则表达式"do*n
"将会和"doon
"和"don
"匹配。该正则表达式还可以与"dn
"匹配,这是由于*
还可以代表空实例+
用于匹配该符号之前任意数量的实例。你会经常看到该字符和括号一起使用,从而创建了一种子表达式。举例来说,正则表达式"(dn)+o
"可以与"dndndndno
"匹配,这是由于该正则表达式可以重复匹配子表达式"dn
"\
(反斜杠)是正则表达式转义字符。{2}
用于匹配该字符之前特定数量的实例。比如,\d{1}
用于匹配1
个数字,使用{2,}
匹配2
或多个
数字,使用{1,3}
匹配至少1
个但不超过3
个实例^
用于匹配字符串开始部分。比如,正则表达式"d.n
"既可以匹配"don
",又可以匹配"pteranodon
"。而正则表达式"^d.n
"只能匹配"don
",而无法匹配"pteranodon
"。这是由于^
使得匹配只能从字符串开始部分匹配,而^
和[]
共同使用时表达式匹配的反义$
用于匹配字符串结尾部分。比如,正则表达式".icks
"既可以与"hicks
"匹配,也可以与"sticks
"(本例中匹配的其实是"ticks
")匹配,还可以与"Dickson
"匹配。但正则表达式.icks$
无法与"Dickson
"匹配,这是因为$
表示字符"s
"应该是该字符串的最后一个字符
正则表达式还有大量内容,但是这些内容足够你完成基本工作。让我们来看一些正则表达式的例子。
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
可以匹配IPv4
地址的模式,但该表达式可以接收“432.567.879.000
”这样的非法地址,也可以接收“192.168.23.24
”这样的合法地址\\\\\w+(\\\w+)+
可以匹配通用命名惯例(UNC
)路径。大量的反斜杠使得该正则表达式难以阅读,这也是为什么在将正则表达式部署到生产环境之前对正则表达式进行调试和调整\w{1}\.\w+@company\.com
可以匹配特定类型的电子邮件地址:首先是要给字母,然后句号,最后是@company.com
。比如d.jones@company.com
可以与该正则表达式进行匹配,donald.jones@company.com.org
也能匹配。该正则表达式允许匹配文本的开始或结尾存在额外的字符,所以可以考虑使用^
或$
通过-Match和Select-String使用正则表达式
PowerShell
包含了一个比较运算符-Match
,以及一个区分大小的版本-Cmatch
,通过这两个运算符与正则表达式进行比较。
"don" -match "d[aeiou]n"
# 结果:True
"dooon" -match "d[aeiou]n"
# 结果:False
"dooon" -match "d[aeiou]+n"
# 结果:True
"djjnn" -match "d[aeiou]+n"
# 结果:False
"dean" -match "d[aeiou]n"
# 结果:False
虽然使用正则表达式的方法很多,但我们主要依靠-Match
测试正则表达式并确保正则表达式能够正确生效。
接下来我们看一个例子,从iis
日志文件中查找错误。具体如下:
Get-ChildItem -Filter *.log -Recurse | Select-String -Pattern "\s40[0-9]\s" | Format-Table FileName,LineNumber,Line -Wrap
该示例递归查找当前文件夹下所有以log
结尾的日志文件,然后通过Select-String
匹配正则表达式进行筛选,最后用Format-Table
格式化输出结果。
接下来看一个Windows
安全日志的例子。
Get-EventLog -LogName Security | Where-Object { $_.EventId -EQ 4624 } | Select-Object -ExpandProperty Message | Select-String -Pattern "WIN[\W\w]+TM[234][0-9]\$"
Get-EventLog -LogName Security | Where-Object { $_.EventId -EQ 4624 -And $_.Message -Match "WIN[\W\w]+TM[234][0-9]\$" }
第一个命令输出符合要求的字符串,即Message
字符串详细;而第二个命令是输出匹配的所有Event
对象,接下来使用的命令取决于希望输出的结果。
额外的提示,技巧以及技术
PowerShell Profile脚本、自定义提示及颜色调整
在PowerShell
中,并没有默认的Profile
脚本存在,你创建的Profile
脚本依赖于你期望该脚本的工作方式。下面我们来看下控制台宿主尝试载入的一些文件(Prifile
脚本),以及尝试载入这些文件的顺序。
(1) $PsHome/Profile.PS1——不管使用任何托管应用程序,计算机上的所有用户都会执行该脚本
(2) $PsHome/Microsoft.PowerShell_Profile.PS1——如果该计算机上的用户使用了控制台宿主,那么就会执行该脚本。如果他们使用的是ISE
,则会使用$PsHome/Microsoft.PowerShellISE_Profile.PS1
脚本
(3) $Home/Documents/WindowsPowerShell/Profile.PS1——无论用户使用的是何种托管应用程序, 只有当前用户会执行该脚本(因为该脚本存在于用户的根目录下)
(4) $Home/Documents/WindowsPowerShell/Microsoft.PowerShell_Profile.PS1——只有当前使用PowerShell
控制台的用户才会执行该脚本。如果用户使用的是PowerShell
的ISE
,那么执行$Home/Documents/WindowsPowerShell/Microsoft.PowerShellISE_Profile.PS1
脚本
如果上面的脚本中其中一个或几个不存在,那么也没有关系。托管程序会跳过不存在的脚本,继续寻找下一个脚本。
PowerShell
提示——也就是你在其控制台中看到的PS C:\Users\Administrator>
,是由一个名为提示(Prompt
)的内置函数产生的。如果你希望替换,只需要替换该函数即可。默认函数如下。
Fuction Prompt
{
$(IF (test-Path Variable:/PSDebugContent) { '[DBG]:' } ELSE { '' }) + 'PS' + $(Get-Location) `
+ $(IF ($NestedPromptLevel -GE 1) { '>>' }) + '>'
}
我们可以自定义一个函数,将其写入到任意Profile
脚本中,这样可以保证后续新开启的Shell
进程都会使用这新的提示。
Funciton Prompt
{
$Time = (Get-Date).ToShortTimeString()
"$Time [$ENV:COMPUTERNAME]:> "
}
至于各种提示信息字体颜色等的调整,我们更建议使用属性中的【颜色】标签,在其中进行可视化的操作。
运算符:-AS、-IS、-Replace、-Join、-Split、-IN、-Contains
-AS
运算符会将一种已存在的对象转换为新的对象类型,从而产生一个新的对象。
1000 / 3 -AS [int]
中括号中包含转换之后的类型,这些类型还可以是[String]
、[XML]
、[Single]
、[Double]
、[Datetime]
等。从技术上讲,示例中的转换是指将小数部分通过四舍五入方式转换为整数,而不是简单将小数部分去掉。
-IS
运算符主要是判断某个对象是否为特定类型,如果是则返回True
,否则False
。
123.354 IS [INT]
"SERTSD" -IS [String]
$True -IS [Bool]
(Get-Date) -IS [DateTime]
-Replace
运算符主要用于在某个字符串中寻找特定字符(串),最后将该字符(串)替换为新的字符(串)。
"192.168.34.12" -Replace "34","15"
# 结果:192.168.15.12
-Join
和-Split
运算符主要用作将数组转化为分隔列表和将分隔列表转化为数组。
$Array ="One","Two","Three","Four","Five"
$Array -Join "|"
# 输出: One|Two|Three|Four|Five
$Array = (Get-Content Computers.tdf) -Split "`t"
# 输出:
# server2
# Windows
# East
# Managed
-Like
运算符用于进行通配符比运算。-Contains
运算符主要用作在一个集合中查找是否存在特定对象。
'this' -Like '*his*'
$Collection = 'abc','def','fhi','jkl'
$Collection -Contains 'abc'
# True
$Collection -Contains 'xzy'
# False
-IN
运算符实现相同的功能,但是会颠倒运算对象的顺序。也就是说,集合在右边,而需要检查的对象在左边。
$Collection = 'abc','def','fhi','jkl'
'abc' -IN $Collection
# True
'xyz' -IN $Collection
# False
字符串处理
在PowerShell
中,字符串是对象,所有就会存在很多方法。可以通过管道传递给Get-Member
查看该对象可用的方法。常用的方法如下。
- IndexOf() 会返回特定字符在字符串中的位置。
"Server-R2".IndexOf("-")
# 6
- Splite()、Join() 和 Replace() 类似上面讲的
-Splite
、-Join
和-Replace
。我们更倾向于使用与算法而不是字符串方法 - ToLower() 和 ToUpper() 可将字符串转换为小写或大写
$computername = "SERVER-R2"
$computername.ToLower()
# server-r2
- Trim() 会将一个字符串的前后空格去掉;TrimStart() 和 TrimEnd() 会将一个字符串的前面或后面的空格去掉
$UserName = " Don"
$UserName.Trim()
# Don
日期处理
Date
对象也包含很多方法,通过这些方法,可用对日期和实际进行处理和计算。
通过上面列表中的属性,可以访问一个DateTime
对象的部分数据,比如日期、年或日等。通过方法可以实现两个功能:计算或者将DateTime
转换为其他格式。例如获取90
天之前的日期,使用AddDays()
方法和一个负数参数实现。名称中以To
开头的方法可以实现将日期以及时间转换为某种特定格式,如短日期类型。另外,需要注意的,这些方法都是依赖于当前计算机本地的区域设定——区域设定决定了日期和时间格式。
$Today = Get-Date
$Today.Month
$90DayAgo = $Today.AddDays(-90)
$90DayAgo
$90DayAgo.ToShortDateString()
而在WMI
中存储的日期和时间格式都难以直接利用。比如,Win32_OperatingSystem
类主要用来记录计算机上一次启动时间,其日期和时间格式如下。
Get-WMIObject Win32_OperatingSystem | Select-Object LastBootUpTime
# 结果
# LastBootUpTime
# ----------------
# 20150317090459.125599+480
PowerShell
的设计者指定直接使用这些信息很困难,所以提供了一组转换方法(通过管道传递给Gm
可查看)。这个两个方法是:ConvertFromDateTime()
和ConvertToDateTime()
方法。
$OS = Get-WMIObject Win32_OperatingSystem
$OS.ConvertToDateTime($OS.LastBootUpTime)
设置参数默认值
大多数PowerShell
命令至少都有几个参数包含默认值。比如,运行Dir
命令,默认会指向当前路径,而并不需要指定-Path
参数。在PowerShell v3
及之后版本,可以对任意命令的任意参数——甚至指定多个命令,指定自定义的默认值。当运行不带有指定参数的命令时,才会采用设定的默认值;但是当运行命令时手动指定了参数以及对应值,之前设定的默认值会被覆盖。默认值保存在名为$PSDefaultParameterValues
的特殊内置变量中。每次打开一个新的PowerShell
窗口时,该变量均设置为空,之后使用一个哈希表填充该变量(可以使用Profile
脚本使得默认值始终有效)。
例如,你希望创建一个包含用户名和密码的凭据对象,然后将该对象设置为所有对象中-Credential
参数的默认值;或者仅仅希望Invoke-Command
Cmdlet
每次运行时都提示需要凭据,此时不要分配默认值,而是分配一段执行Get-Credential
命令的脚本块。
# 第一个
$Credential = Get-Credential -UserName Administrator -Message "Enter Admin Credential"
$PSDefaultParameterValues.Add('*:Credential',$Credential)
# 第二个
$PSDefaultParameterValues.Add('Invoke-Command:Credential',{ Get-Credential -Message 'Enter Administrator Credential' -UserName Administrator })
# 查看变量内容
$PSDefaultParameterValues
可以看到该Add()
方法的基本格式:第一个参数为 <Cmdlet>:<Paremeter>
,该<Cmdlet>
可以接受 *
等通配符;第二个参数或者直接给出默认值,或者是执行一个或多个命令的脚本块。
学习脚本块
脚本是PowerShell
的一个关键知识点。之前你可能已经简单使用过脚本了。
Where-Object
命令的-FilterScript
参数会使用脚本块ForEach-Object
命令的-Process
参数会使用脚本块- 使用
Select-Object
创建自定义属性的哈希表或者使用Format-Table
创建自定义列的哈希表,都会需要一个脚本块作为E
或者Expression
的键值 - 参数的默认值也可以是一个脚本块
- 针对一些远程处理以及
Job
相关命令,比如Invoke-Command
和Start-Job
命令,也需要一个脚本块作为-ScriptBlock
参数的值
简单的说,脚本块是指包含在大括号中的全部命令——哈希表除外(哈希表在大括号之前会带有@
符号)。你可以在命令行中输入一个脚本块并赋值给一个变量,再使用&
该调用运算符来执行该脚本块。
$Block = { Get-Process | Sort-Object -Property Vm -Descending | Select-Object -First 10 }
&$Block
最后,我们来说下使用他人的脚本所采用的方式——逐行检查。当你逐行查阅每一行时,完成下面的工作。
- 识别其中的变量,并找出其对应的值,之后将它们写在一张纸上。因为大部分情况下,变量都会被传递给某些命令,所以记下每个变量可能的值会帮助你预测每个命令的作用
- 当你遇到一些新的命令时,请阅读对应的帮助文档,这样可以理解这些命令的功能。针对
Get-
类型的命令,尝试运行它们——将脚本中变量的值传递给命令的参数——来查看这些命令的输出结果 - 当你遇到不熟悉的部分时,比如
[Environment]
,请考虑在虚拟机中执行简短的代码片段来查看该片段的功能(使用虚拟机有助于保护你的生产环境)。可以通过在帮助文档中搜寻(使用通配符)这些关键字来查阅更多的信息
总结
以上是对PowerShell
的其他相关知识介绍,至此这个系列也就结束了。