PowerShell 技能连载 - 危险的临时文件!
内部的系统功能往往十分有用,但请确保真正了解它们的功能。
一个特别常见的系统方法叫做 GetTempFileName()
,能够创建临时文件名。而当您进一步观察的时候,您会发现它不仅创建临时文件名,而且还创建了临时文件:
1 | $file = [System.IO.Path]::GetTempFileName() |
所以如果在脚本中只是使用这个方法来创建临时文件名的话,会留下一大堆孤立的文件。
内部的系统功能往往十分有用,但请确保真正了解它们的功能。
一个特别常见的系统方法叫做 GetTempFileName()
,能够创建临时文件名。而当您进一步观察的时候,您会发现它不仅创建临时文件名,而且还创建了临时文件:
1 | $file = [System.IO.Path]::GetTempFileName() |
所以如果在脚本中只是使用这个方法来创建临时文件名的话,会留下一大堆孤立的文件。
在 PowerShell 中,一切都是用对象描述。以下是一个检查任意对象并将它的成员以文本的方式复制到剪贴板的单行代码:
1 | "Hello" | |
只需要将 “Hello” 替换成任何变量或命令,然后看看复制了什么到剪贴板中。您可以将信息粘贴到文本编辑器或文字处理器中,并将它打印出来或转成 PDF 备用。
在 PowerShell 5 中,对枚举的新支持特性使得处理比特位比您在前面的 PowerShell 技能中看到的简单得多。现在设置或清除比特位不再需要冗长的逻辑操作符。
我们先定义一个枚举类型,这样更好管理十进制数:
1 | #requires -Version 5 |
现在,十进制数的比特位可以很容易地转化为 GardenPartyItem 的列表:
1 | PS C:\> [GardenPartyItems]11 |
注意:将十进制数转换为枚举型时,请确保枚举型中定义了所有的比特。如果十进制数太大,包含枚举型之外的比特时,转换会失败。
要增加一个新的标志位,请试试以下的代码:
1 | PS C:\> $flags |
要移除一个标志位,请试试以下代码:
1 | PS C:\> $flags |
然而,实际上并没有看起来这么简单。当移除一个已有的标志位,没有问题。但移除一个没有置位的标志位,会把比特值搞乱:
1 | PS C:\> $flags |
所以 PowerShell 在自动处理二进制算法方面明显还不够智能。要安全地使用该功能,您还是要用二进制操作符。要移除标志位,请使用 -band
和 -bnot
:
1 | PS C:\> $flags |
要设置标志位,请使用 -bor
:
1 | PS C:\> $flags |
在所有这些操作中,实际上是在操作一个十进制数:
1 | PS C:\> [Int]$flags |
相当棒,对吧?
对十进制数设置比特标志位不是很难,但是不够直观。以下是一个快速的新方法,演示如何设置或取消一个数字中特定的比特:
1 | $decimal = 6254 |
结果演示了代码做了什么。ToString()
从右到左显示比特,所以第 0 比特是在最右边。在第二行和第三行,设置了两个独立的比特位,而并不影响其它位。在最后一行中,清除了一个比特位。
1 | 1100001101110 |
在前一个技能中我们演示了如何使用 PowerShell 5 新的枚举特性来解析bite标志位,甚至可以独立地检测每个标志位。
如果您无法使用 PowerShell 5,在早期的 PowerShell 版本中,仍然可以使用这个技术只需要通过 C# 代码来定义枚举即可:
1 | # this is the decimal we want to decipher |
如您所见,从十进制数转换到新的枚举类型使用正常而且非常简单:
1 | PS C:\> [BitFlags]6625 |
有时候您会需要处理比特标志位值。一个数字中的每个比特代表一个特定的设置,并且您的代码可能需要决定一个标志位是否置位,而不能影响别的比特。
这常常需要一系列位操作。然而在 PowerShell 5 中,有一个简单得多的办法——标志位枚举。
假设有一个值 56823,并且希望知道哪个比特是置位的。您需要将该数字转换成可视化的比特:
1 | PS C:\> [Convert]::ToString(56823, 2) |
如果您了解每个比特的意义,那么一个更强大的方法是定义一个枚举:
1 | #requires -Version 5 |
对每个比特提供一个友好的名字,并且记得添加属性 [Flags]
(这将允许设置多个值)。
现在要解析这个十进制值非常简单——只需要将它转换成新定义的枚举类型:
1 | $rawflags = 56823 |
这时得到的结果:
1 | PS C:\> $flags |
如果您只希望检测某个标志位是否置位,请使用 HasFlag()
方法:
1 | PS C:\> $flags.HasFlag([CustomBitFlags]::Option1) |
泛型可以作为实际类型的占位符,您可能会好奇为什么它会有意思。
有许多不同的数据类型没有 NULL 值。例如 Integer 和 Boolean 型,没有办法指出一个值是非法的还是未设置。您可以通过将一个 0(或者 -1)指定为某个 integer 变量的 “undefined” 值。但如果所有的数字都是合法的值呢?对于 Boolean,情况也是一样:虽然您可以定义 $false
值为 “undefined” 值,但许多情况下的确需要三种值:$true
、$flase
和 undefined
。
泛型是解决的办法,您可以使用 Nullable
类型根据任何合法的类型来创建自己的可空值类型。
1 | [Nullable[int]]$number = $null |
用常规数据类型来做数据转换:
1 | PS C:\> [int]$null |
以下是在 PowerShell 5 中使用新的类特性的另一个用例。在前一个例子中,我们演示了如何从 System.Diagnostics.Process
派生一个新类,从而获得代表进程的功能更强大的对象。
以下是一个从 WebClient
派生的类,WebClient
主要是用来连接网站。当您使用标准的 WebClient
对象是,它拒绝连接到证书错误的 HTTPS 网站。这是一件好事,但是有时候您仍需要连接这类网站。
1 | #requires -Version 5 |
这样,”“MyWebClient”
“ 类继承于 WebClient()
并改变了 ServerCertificateValidationCallBack
的行为。它只是返回 $true
,所以所有的连接都是成功的,而且证书检验变得无关紧要。
PowerShell 5 内置了类的支持。您可以使用这个新特性来增强已有的 .NET 类的功能。以下是一个例子:创建一个包含新功能的增强的进程类。
进程通常是由 System.Diagnostics.Process
对象代表。它们只有有限的功能,并且假设没有能直接使用的以友好方式关闭一个应用程序的方法。您可以杀除进程(会丢失未保存的数据),或关闭它(用户可以取消关闭)。
以下是一个新的 继承于 System.Diagnostics.Process
的名为 AppInstance
的类。所以它拥有 Process
类中所有已有的功能,您可以增加额外的属性和方法:
1 | #requires -Version 5 |
如您在这个例子中所见,当您从这个类创建一个新实例时,它启动了一个新的进程,而且这些进程照常暴露出相同的属性和方法。而且,有一些新的例如 SetPriority()
和 Close()
的新方法。
PowerShell 可以调用 Windows 内部的 API,在这个例子中,我们想向您展示如何改变一个应用程序窗口的显示状态。比如可以最大化、最小化、隐藏或显示窗口。
这个例子使用 PowerShell 5 最新的枚举特性对 showstate
数值赋予有意义的名字。在 PowerShell 的更早版本中,只需要移除枚举部分,并在代码中直接使用合适的 showstate
数字即可。
这里的学习要点是如何使用 Add-Type
来包装一个 C# 形式的 API 方法并在 PowerShell 代码中返回一个暴露这个方法的 type:
1 | #requires -Version 5 |
请注意这个例子将 PowerShell 窗口临时隐藏 2 秒钟。您可以对任何运行中的应用程序窗口做相同的事情。只需要用 Get-Process
来查找目标进程,并使用它的 “MainWindowHandle
“ 属性来发送 showstate 改变请求。
一些应用程序有多个窗口。在这种情况下,您只能针对主窗口操作,否则需要先靠其它 API 来获取子窗口的句柄集合。