自定义Shell界面
每一个PowerShell进程开启时都是一样的:一样的别名,一样的PSDrives,一样的色彩等。为什么不使用自定义的Shell界面呢?
PowerShell Profile脚本
PowerShell的托管应用程序,比如PowerShell ISE的控制台,是指将命令发送至PowerShell引擎的一种方式。首先PowerShell引擎会执行命令,然后托管应用程序再显示执行结果。托管应用程序的另一个功能是当新开一个Shell窗口时,载入和运行Profile脚本。
这些Profile脚本可被用作自定义PowerShell的运行环境——能够自定义的包括:载入SnapIn管理单元或模块,切换到另外的根路径,定义需要使用的功能等。
例如,下面是一个Profile脚本。
该Profile载入了常用的两个Shell的扩展程序,并修改根路径为C盘。
在PowerShell中,并没有默认的Profile脚本存在,创建的Profile脚本会依赖于你期望该脚本的工作方式。请执行Help About_Profiles
,查看详细信息。
在常规控制台和PowerShell ISE中来回切换时,会希望这两种托管应用程序都运行相同的Profile脚本,所以需要确保在正确的路径下创建正确的Profile脚本。
下面是控制台宿主尝试载入的一些文件,以及尝试载入这些文件的顺序。
1)$PsHome/Profile.PS1
:不管使用何种托管应用程序,计算机上的所有用户都会执行该脚本(请记住,PowerShell已经预定义了$PSHome,该变量包含PowerShell的安装文件夹的路径)。
2)$PsHome/Microsoft.PowerShell_Profile.PS1
:如果用户使用控制台宿主,那么就会执行该脚本。
- 如果使用的是PowerShell的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
。
如果上面脚本中某一个或者几个不存在,那么也没关系。托管应用程序会跳过不存在的脚本,继续寻找下一个可用的脚本。
Tips:
- 在64位操作系统上,由于存在独立的32位与64位的PowerShell程序,所以脚本也会包括32位与64位的版本。
- 请不要期望相同的脚本在32位与64位PowerShell中都能正常运行。这意味着,某些模块或者扩展程序仅在某一个架构中才可用,所以请不要尝试使用一个32位的Profile脚本,将某个64位的模块载入32位的PowerShell中,因为这根本不可能成功。
下面是针对该列表的其他一些知识点:
$PsHome
是包含PowerShell安装路径信息的内置变量;- 在大部分操作系统中,该变量的值是C:\Windows\System32\WindowsPowerShell\V1.0(64位版本的PowerShell)。
$Home
是另一个内置的变量,该变量指向当前用户的配置文件夹(比如C:\Users\Administrator)。- 在前面的列表中,使用“Documents”表示文档文件夹,但是在某些版本的Windows系统中可能是“My Documents”。
因为期望将相同的Shell扩展程序载入到PowerShell,而不管是控制台还是ISE,所以我们选择自定义$Home\Documents\WindowsPowerShell\Profile1.PS1
。因为该Profile脚本在两种托管应用程序中都可以运行。
请记住,Profile脚本也仅是脚本而已,它会依赖于PowerShell的当前执行策略。如果设置的执行策略是Restricted
,那么Profile脚本就无法运行;如果设置的执行策略是AllSigned,那么Profile脚本必须经过签名才能运行。
自定义提示
PowerShell提示,如PS C:>这类字符,是由一个名为提示(Prompt)的内置函数产生的。如果希望自定义该提示,只需要替换该函数即可。可以在Profile脚本中定义一个新的提示函数,这样在每次打开Shell界面的时候都可以采用新的提示函数。
下面是默认的提示函数:
该函数首先会检测$DebugContext
变量,是否被预定义在PowerShell的Variable:Drive
中。如果有,那么该函数就会将[DBG]
:添加到提示启动阶段。否则,该提示会被定义为PS再加上由Get-Location
Cmdlet返回的当前路径(比如PS D:\Test>)。如果该Shell处于嵌套提示中——由内置函数$NestedPromptLevel
返回,那么提示中会添加“>>”字样。
下面是自定义的一个提示函数。可以直接将该函数加入到任意Profile脚本中,这样可以保证后续新开启的Shell进程,都会将该提示作为一个标准提示函数使用。
function Prompt {
$Time = (Get-Date).ToShortTimeString()
"$Time [$env:COMPUTERNAME] $(Get-Location)> "
}
调整颜色
默认的文本前景色与后景色,都可以通过单击PowerShell命令窗口左上角的边框来修改。
修改错误、警告以及其他信息的颜色略微有点复杂,需要通过运行命令才能实现。但是可以将这部分命令放到Profile脚本中,这样每次进入PowerShell时,都会执行这些命令。
比如可以将错误消息的前景色修改为绿色:(Get-Host).PrivateData.errorforegroundcolor="green"
可以通过命令修改下列设置的颜色:
- ErrorForegroundColor
- ErrorBackgroundColor
- WarningForegroundColor
- WarningBackgroundColor
- DebugForegroundColor
- DebugBackgroundColor``
- VerboseForegroundColor
- VerboseBackgroundColor
- ProgressForegroundColor
- ProgressBackgroundColor
下面是可以选择的几种颜色:
Red,Yellow,Black,White,Green,Cyan,Magenta,Blue
同时,也存在这些颜色的对应深色颜色:DarkRed, DarkYellow, DarkGreen, DarkCyan, DarkBlue等。
运算符-AS、-IS、-Replace、-Join、-Split、-IN、-Contains
–AS和–IS
-AS
运算符会将一种已存在的对象转换为新的对象类型,从而产生一个新的对象。例如,如果存在一个包含小数的数字(可能来自一个除法计算),可以通过Converting或者Casting将该数字转化为一个整数。
语句的结构:首先是一个将被转换的对象,然后是-AS运算符,最后是一个中括号,中括号中包含转化之后的类型。这些类型可以是[String]
、[XML]
、[INT]
、[Single]
、[Double]
、[Datetime]
等。在该示例中,将数值转化为整数是指将小数部分通过四舍五入方式转为整数,而并不是简单地将小数部分去掉。
-IS
运算符通过类似方式实现。该运算符主要用于判断某个对象是否为特定类型,如果是,则返回True,否则为False。
–Replace
-Replace
运算符主要用于在某个字符串中寻找特定字符(串),最后将该字符(串)替换为新的字符(串)。
命令的结构:首先是源字符串,之后为-Replace运算符。然后需要提供在源字符串中寻找的字符(串),最后跟上一个逗号外加最新的字符(串)。
–Join和-Split
-Join和-Split运算符,主要用作将数组转化为分隔列表和将分隔列表转化为数组。
例如,存在包含5个元素的数组,将数组里的值转换为以管道符隔开的字符串:-join
。
PS C:\> $array="one", "two", "three", "four", "five"
PS C:\> $array
one
two
three
four
five
PS C:\> $array -join "|"
one|two|three|four|five
# 可以将该执行结果存入一个变量,进行复用
PS C:\> $string = $array -join "|"
PS C:\> $string
one|two|three|four|five
# 导出文件
PS C:\> $string | Out-File Data.dat
可以使用-Split
运算符实现相反的效果:它会从一个分隔的字符串中产生一个数组。
PS C:\> echo "How are you" >> d:\file.txt
PS C:\> gc D:\file.txt
How are you
PS C:\> $array = (gc D:\file.txt) -split "`t" # 以制表符,进行分割
PS C:\> $array # 未匹配,所以未生效
How are you
PS C:\> $array = (gc D:\file.txt) -split " " # 以空格,进行分割
PS C:\> $array
How
are
you
# 通过索引查询单个元素
PS C:\> $array[1]
are
–Contains和-In
-Contains运算符和-like,容易混淆。
-Like
运算符用于进行通配符比较运算。-Contains
运算符主要用作在一个集合中查找是否存在特定对象。
-In
运算符实现的功能,会颠倒运算对象的顺序。也就是说,集合在右边,而需要检查的对象在左边。
字符串处理
假如有一个字符串,需要将该字符串全部转化为大写,或者需要取得该字符串的最后3个字符。那么该如何实现?
在PowerShell中,字符串是对象,所以就会存在多种方法(Method)。方法是通知对象去做某项工作的方式,通常是针对对象本身。可以将该对象通过管道发送给Gm查看该对象可用的方法。
下面是一些比较有用的String方法:
1)IndexOf()
会返回特定字符在字符串中的位置。
2)Split(), Join()和Replace()类似于-Split, -Join和-Replace。但是更倾向于使用PowerShell的运算符。
3)ToLower()和ToUpper()可以将字符串转化为小写或大写。
4)Trim()会将一个字符串的前后空格去掉;
- TrimStart()和TrimEnd()会将一个字符串的前面或者后面的空格去掉。
日期处理
和String类型对象一样,Date对象也包含多个方法。通过这些方法,可以对日期和时间进行处理和计算。
范例:
1)访问一个DateTime的部分数据,比如日期、年或者月。
2)获取90天之前的日期,使用AddDays()
方法和一个负数参数实现。
3)名称中以“To”开头的方法可以实现将日期以及时间转化为某种特定格式,比如短日期类型。处理WMI日期
在WMI中存储的日期和时间格式都难以直接利用。例如,Win32_OperatingSystem
类主要用来记录计算机上一次启动的时间,其日期和时间格式如下。 ```python PS C:> Get-WmiObject Win32_OperatingSystem | select LastBootUpTime
LastBootUpTime
20220620103813.500000+480
将WMI对象通过管道发送给Gm,请注意观察最后两个方法。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/635565/1656142776254-5b2b79ad-0dc7-4b68-823b-b15283c6a7e5.png#clientId=u97601627-727e-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=434&id=ubcbb1f8a&margin=%5Bobject%20Object%5D&name=image.png&originHeight=543&originWidth=872&originalType=binary&ratio=1&rotation=0&showTitle=false&size=80290&status=done&style=none&taskId=ubb353b7b-50d9-435c-80aa-95f976a532f&title=&width=697.6)<br />在该示例中,获取到的是WMI的日期和时间。假如需要转化为正常的日期和时间格式,请参照下面的命令:
```python
PS C:\> $OS = Get-WmiObject Win32_OperatingSystem
PS C:\> $OS.ConvertToDateTime($OS.LastBootUpTime)
2022年6月20日 10:38:13
如果你期望将正常的日期和时间信息放入到一个正常表中,可以通过Select-Object或者Format-Table命令创建自定义计算列以及属性。
PS C:\> Get-WmiObject Win32_OperatingSystem | select BuildNumber, __Server, @{n='LastBootTime'; e={$_.ConvertToDateTime($_.LastBootUpTime)}}
BuildNumber __SERVER LastBootTime
----------- -------- ------------
19042 IM20211010 2022/6/20 10:38:13
设置参数默认值
大多数PowerShell命令至少都有几个参数包含默认值。例如,运行Dir命令,默认会指向当前路径,而并不需要指定-Path参数。
在PowerShellv3版及之后的版本,可以对任意命令的任意参数——甚至是针对多个命令,指定自定义的默认值。当运行不带有指定参数的命令时,才会采用设定的默认值;但是当运行命令时手动指定了参数以及对应值,之前设定的默认值会被覆盖。
默认值保存在名为$PSDefaultParameterValues
的特殊内置变量中。当每次新开一个PowerShell窗口时,该变量均置空,之后使用一个哈希表填充该变量(可以通过Profile脚本使得默认值始终有效)。
例如,创建一个包含用户名以及密码的凭据对象,然后将该对象设置为所有命令中-Credential
参数的默认值。
如果仅希望Invoke-Command Cmdlet每次运行时都提示需要凭据,此时请不要直接分配一个默认值,而是分配一段执行Get-Credential命令的脚本块。Add()
方法的基本格式:
- 第一个参数为
<Cmdlet>:<Parameter>
,该可以接受*等通配符。 - 第二个参数,可以是直接给出的默认值,或者是执行其他(一个或多个)命令的脚本块。
查看$PSDefaultParameterValues
包含的内容:
可以通过PowerShell中的About_Parameters_Default_Values
帮助文档查看该特性更多的知识点。
作用域补充说明
PowerShell的变量由作用域(Scope)控制,同时作用域也会影响参数的默认值。
如果在命令行中设置了$PSDefaultParameterValues
,那么该参数会针对本Shell会话中的所有脚本以及命令起作用。但如果仅在一段脚本中设置了$PSDefaultParameterValues,那么只会在该脚本作用域中有用。
这意味着在一段脚本中设置多个参数的默认值,但是并不影响其他脚本或者Shell会话的运行。作用域的核心思想是“无论脚本发生了什么,仅会影响该脚本”。
脚本块
脚本块是PowerShell的一个关键知识点,如下:
- Where-Object命令的
-FilterScript
参数会使用脚本块。 - ForEach-Object命令的
-Process
参数会使用脚本块。 - 使用Select-Object或Format-Table创建自定义属性或列的哈希表,都会需要一个脚本块作为E或者Expression的键值。
- 参数的默认值也可以是一个脚本块。
- 针对一些远程处理以及Job相关的命令,比如Invoke-Command和Start-Job命令,也需要一个脚本块作为
-ScriptBlock
参数的值。
那么,什么是脚本块呢?简单来讲,脚本块是指包含在大括号中的全部命令——哈希表除外(哈希表在大括号之前会带有@符号)。你可以在命令行中输入一个脚本块,然后将该脚本块赋值给一个变量,再使用调用运算符&来执行该脚本块。
可以使用脚本块完成更多的工作。如果希望进一步学习脚本块,请参阅PowerShell中的About_Script_Block
帮助文档。
其他学习途径
可以订阅我们的Twitter:@jeffhicks
和@concentrateddon
。我们会定期在Twitter上分享一些有用的提示以及小技巧。PowerShell.Org网站上也提供邮件列表定期推送一些小技巧,别忘了还有PowerShell.Org的论坛。