PowerShell 技能连载 - 将值保存到 Excel 工作表中
有些时候,您可能会需要更新一个 Excel 工作表中的值。PowerShell 可以操作 Excel 对象模型,不过它很慢。以下是一个打开 Excel 文件,然后写入信息到 A1 单元格,最后保存更改的例子,
请确保您调整了路径,指向一个实际存在的 Excel 文件。
1 | $excel = New-Object -ComObject Excel.Application |
有些时候,您可能会需要更新一个 Excel 工作表中的值。PowerShell 可以操作 Excel 对象模型,不过它很慢。以下是一个打开 Excel 文件,然后写入信息到 A1 单元格,最后保存更改的例子,
请确保您调整了路径,指向一个实际存在的 Excel 文件。
1 | $excel = New-Object -ComObject Excel.Application |
有些时候,您可能会需要从 Excel 工作表中读取信息。PowerShell 可以操作 Microsoft Excel 对象模型,虽然它的速度很慢。
以下是一段延时如何操作 Excel 单元格的示例代码。请确保您调整了以下代码中的路径,指向一个实际存在的 Excel 文件。该代码将读取 A1 单元格的内容:
1 | $excel = New-Object -ComObject Excel.Application |
PowerShell 只能在它的进程空间里设置环境变量,所以这些改变无法保存,并且在 PowerShell 之外不可见。
要永久性地设置环境变量,可以编写一个简单的函数:
1 | function Set-EnvironmentVariable |
现在您可以这样设置环境变量:
1 | PS> Set-EnvironmentVariable -Name test -Value 123 -Target User |
您也可以传入空字符串来移除一个环境变量。
1 | PS> Set-EnvironmentVariable -Name test -Value "" -Target User |
这是为什么 -Value
参数定义加上 [AllowEmptyString()]
属性的原因。如果没有这个属性,一个必选参数不能接受一个空字符串,那么该函数就无法移除环境变量。
另一个值得注意的地方是 -Target
参数的类型定义:因为制定了一个枚举类型,所以当您在 PowerShell ISE 或其它带有智能提示的编辑器中使用这个函数时,该编辑器将会贴心地提供智能提示选择。
以下是一行常常迷惑 PowerShell 用户的代码:
1 | Get-Service | Select-Object -ExcludeProperty Name |
当您使用 Select-Object
时,它的 -ExcludeProperty
参数并没有做任何事情。实际上,ExcludeProperty
只在使用 -Property
的时候才有效。所以这行代码是可以用的:
1 | Get-Service | Select-Object -ExcludeProperty Name -Property Status, DisplayName, Name |
然而,这看起来很荒谬:为什么通过 -Property
指定了属性,又还要用 ExcludeProperty
来排除它们呢?这样不是更简单吗:
1 | Get-Service | Select-Object -Property Status, DisplayName |
实际上,-ExcludeProperty
只在使用通配符的时候有意义:
1 | PS> Get-CimInstance -ClassName Win32_BIOS | Select-Object -Property *BIOS* -ExcludeProperty *major*, *minor* |
以下是一段强制关闭 PowerShell ISE 中所有打开的文档的代码片段。请注意:它不经提示就关闭所有的文档。它适用于当您搞砸了,并且不准备保存脚本的情况:
1 | foreach ($tab in $psise.PowerShellTabs) |
不过,当您运行这段代码时,您会收到一个错误。即便您不使用 PowerShell ISE,这个错误(和它的修复信息)对您来说可能十分有趣。
这段代码枚举出所有打开的文件并且尝试逐个关闭它们。这并不能工作:当您枚举一个数组时,您无法改变这个数组。所以当 PowerShell 关闭一个文档时,这个文件列表就变化了,而这将打断这个循环。
当发生这个错误时,一个简单的办法是先将这个数组拷贝到另一个数组。然后就可以安全地枚举这个副本数组。拷贝一个数组十分简单,只需要将它强制类型转换为 [Object[]]
。
以下是正确的代码:
1 | foreach ($tab in $psise.PowerShellTabs) |
这是我们迷你系列的最后一部分,向我们超快的 Test-OnlineFast
函数添加管道功能。您现在可以像这样将计算机名通过管道传给函数:
1 | PS> 1..200 | ForEach-Object { "192.168.189.$_" } | Test-OnlineFast |
当然,您也可以传递普通参数给这个函数:
1 | PS> Test-OnlineFast -ComputerName google.de, microsoft.com, 127.0.0.1 |
您甚至可以使用其它 cmdlet 的结果,假设您选择了希望传给该函数的属性。一下这行代码 ping 您 Active Directory 中的所有计算机(您最好稍微做一下限制,以免耗尽资源):
1 | PS> Get-ADComputer -Filter * | Select-Object -ExpandProperty DnsHostName | Test-OnlineFast |
在前一个技能中我们创建了一个名为 Test-OnlineFast
的高速的新的 PowerShell 函数,它使用 WMI 来高速 ping 任意数量的计算机。今天我们将通过向 ping 的结果增加一些列额外的属性使它变得更有用。
先让我们检查 Test-OnlineFast
是如何工作的。以下是一些例子。我们先 ping 一系列计算机。您既可以使用计算机名也可以使用 IP 地址:
1 | PS> Test-OnlineFast -ComputerName google.de, powershellmagazine.com, 10.10.10.200, 127.0.0.1 |
我们现在 ping 整个 IP 地址段。以下例子是从我们的公共酒店 WLAN 中执行的(请将 IP 范围调整为您所在的网络):
1 | PS> $iprange = 1..200 | ForEach-Object { "192.168.189.$_" } |
神奇的是它的超快速度。ping 整个子网只用了几秒。
现在,我们来看看这个函数。在前面的技能中我们解释了其中的一部分。这个版本向 ping 的结果增加了有用的属性,例如 Online
和 DnsName
,它返回关于 ping 状态的友好文本,而不是幻数。所有这些是通过计算属性的哈希表实现的,基于 ping 返回的原始信息:
1 | function Test-OnlineFast |
在前一个技能中我们演示了如何用 WMI 快速 ping 多台计算机。那么今天我们将它封装为一个可复用的 PowerShell 函数。它可以快速地 ping 一台或多台计算机。
以下是函数代码:
1 | function Test-OnlineFast |
现在要以指定的超时值 ping 多台计算机变得非常简单:
1 | PS> Test-OnlineFast -ComputerName microsoft.com, google.de |
状态码 “0” 代表响应结果:主机在线。其他状态码代表失败。
默认情况下,Test-OnlineFast
的超时时间为 1000 毫秒,所以当一台计算机没有响应时,最多等待 1 秒。您可以通过 -TimeoutMillseconds
参数改变超时值。设置越长的超时值意味着命令的执行时间越长。所以您应该在系统足够响应的范围内使用尽可能短的超时时间。
另一个影响时间的变量是 DNS 解析:如果 DNS 解析速度慢,或者无法解析到名称,将增加总体时间。如果指定 IP 地址,就不会发生这种变慢现象。
以下是在几秒内 ping 200 个 IP 地址的例子:
1 | PS> $ComputerName = 1..255 | ForEach-Object { "10.62.13.$_" } |
在前一个技能中我们演示了如何用 WMI 快速 ping 多台计算机,它的语法比较另类。那么让我们重写代码,使得指定要 ping 的计算机列表变得更容易:
1 | # ping the specified servers with a given timeout (milliseconds) |
现在要 ping 更大量的计算机变得更容易:只要将它们加入 $ComputerName
字符串数组。假如有一个文本文件,每行是一个计算机名,您也可以用 Get-Content
来写入 $ComputerName
变量。
在前一个技能中我们演示了如何用 WMI 以指定的超时值 ping 计算机。WMI 还可以做更多的事:它可以迅速 ping 多台计算机,不过语法有一点另类。
以下是如何 ping 多台计算机:
1 | # ping the specified servers with a given timeout (milliseconds) |