PowerShell 技能连载 - 使用超棒的 Export-Excel Cmdlet(第 1 部分)

Doug Finke 创建了一个非常棒的 PowerShell 模块 ImportExcel,它提供了从 Microsoft Excel 导入和导出数据所需的所有命令。它不需要安装Office。

我们不能涵盖这个模块提供的所有内容,但在本文中,我们将为您提供启动和运行它的基础知识,在后续的技巧中,我们将讨论一些格式化技巧。

要使用 Excel 命令,只需要下载并安装免费的模块:

1
PS> Install-Module -Name ImportExcel -Scope CurrentUser -Force

首次运行时,您可能必须同意下载一个 “NuGet” 开源DLL。命令完成后,您现在可以访问大量新的 Excel 命令,其中最重要的是 Export-Excel

您现在可以通过管道将数据直接传给一个 Excel 文件,并且假设在 Microsoft Office 已经安装的情况下,您甚至可以在 Excel 中打开并显示文件(创建 .xlsx 文件不需要 Office)。

以下是一个简单的示例:

1
2
3
$Path = "$env:temp\report.xlsx"
Get-Process | Where-Object MainWindowTitle |
Export-Excel -Path $path -ClearSheet -WorksheetName Processes -Show

就是这么简单。创建 Excel 文件从来没有这么容易过。不过,你要记住以下几点:

  • 在将数据通过管道传给 Export-Excel 之前,使用 Select-Object 选择要导出的属性。
  • 使用 -ClearSheet 清除以前的数据。如果省略此参数,新数据将附加到 .xlsx 文件中的现有数据之后。
  • 在创建具有相同名称的新文件之前,您可能需要考虑手动删除旧的 .xlsx 文件。否则,Export-Excel 可能会参考旧文件中的现有设置。

PowerShell 技能连载 - 自动创建 HTTP 响应码清单

在前一个示例中我们学习了如何自动将数值型的 HTTP 响应码转换为描述性的文本,只需要将它们转换为 System.Net.HttpStatusCode 即可。

1
2
PS> [System.Net.HttpStatusCode]500
InternalServerError

这是因为 System.Net.HttpStatusCode 是一个所谓的“枚举”,其作用类似于“查找表”。您可以轻松地转储枚举的所有成员,例如创建一个 HTTP 响应代码表:

1
2
3
4
5
6
7
[Enum]::GetValues([System.Net.HttpStatusCode]) |
ForEach-Object {
[PSCustomObject]@{
Code = [int]$_
Description = $_.toString()
}
}

以上是创建一个最常见的 HTTP 响应码所需要的所有代码。

Code Description
---- -----------
 100 Continue
 101 SwitchingProtocols
 200 OK
 201 Created
 202 Accepted
 203 NonAuthoritativeInformation
 204 NoContent
 205 ResetContent
 206 PartialContent
 300 MultipleChoices
 300 MultipleChoices
 301 MovedPermanently
 301 MovedPermanently
 302 Redirect
 302 Redirect
 303 SeeOther
 303 SeeOther
 304 NotModified
 305 UseProxy
 306 Unused
 307 TemporaryRedirect
 307 TemporaryRedirect
 400 BadRequest
 401 Unauthorized
 402 PaymentRequired
 403 Forbidden
 404 NotFound
 405 MethodNotAllowed
 406 NotAcceptable
 407 ProxyAuthenticationRequired
 408 RequestTimeout
 409 Conflict
 410 Gone
 411 LengthRequired
 412 PreconditionFailed
 413 RequestEntityTooLarge
 414 RequestUriTooLong
 415 UnsupportedMediaType
 416 RequestedRangeNotSatisfiable
 417 ExpectationFailed
 426 UpgradeRequired
 500 InternalServerError
 501 NotImplemented
 502 BadGateway
 503 ServiceUnavailable
 504 GatewayTimeout
 505 HttpVersionNotSupported

这种方法适用于您可能遇到的任何枚举。只需更改枚举数据类型的名称即可。这个例子转储可用的控制台颜色代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[Enum]::GetValues([System.ConsoleColor]) |
ForEach-Object {
[PSCustomObject]@{
Code = [int]$_
Description = $_.toString()
}
}



Code Description
---- -----------
0 Black
1 DarkBlue
2 DarkGreen
3 DarkCyan
4 DarkRed
5 DarkMagenta
6 DarkYellow
7 Gray
8 DarkGray
9 Blue
10 Green
11 Cyan
12 Red
13 Magenta
14 Yellow
15 White

PowerShell 技能连载 - 转换 HTTP 响应码

在前一个示例中我们创建了一个小的 PowerShell 函数,它能够检查 Web 网络的可用性,并 HTTP 返回码会作为测试结果的一部分返回。让我们看看如何可以轻松地将这个数字代码转换为有意义的文本消息。

以下还是那个测试网站的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Test-Url
{
param
(
[Parameter(Mandatory,ValueFromPipeline)]
[string]
$Url
)

Add-Type -AssemblyName System.Web

$check = "https://isitdown.site/api/v3/"
$encoded = [System.Web.HttpUtility]::UrlEncode($url)
$callUrl = "$check$encoded"

Invoke-RestMethod -Uri $callUrl |
Select-Object -Property Host, IsItDown, Response_Code
}

以下是典型的结果:

1
2
3
4
5
PS C:\> Test-Url -Url powershellmagazine.com

host isitdown response_code
---- -------- -------------
powershellmagazine.com False 200

在这个示例中,响应代码是 “200”,恰好代表 “OK”。如果您希望将 HTTP 响应码转换为文本,只需要将数据类型转换为 [System.Net.HttpStatusCode]。这样就可以了:

1
2
PS C:\> 200 -as [System.Net.HttpStatusCode]
OK

以下是包含该转换过程的版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function Test-Url
{
param
(
[Parameter(Mandatory,ValueFromPipeline)]
[string]
$Url
)

Add-Type -AssemblyName System.Web

$check = "https://isitdown.site/api/v3/"
$encoded = [System.Web.HttpUtility]::UrlEncode($url)
$callUrl = "$check$encoded"
$response = @{
Name = 'Response'
Expression = {
'{0} ({1})' -f
($_.Response_Code -as [System.Net.HttpStatusCode]),
$_.Response_Code
}
}
Invoke-RestMethod -Uri $callUrl |
Select-Object -Property Host, IsItDown, $response
}

结果如下:

1
2
3
4
5
PS C:\> Test-Url -Url powershellmagazine.com

host isitdown Response
---- -------- --------
powershellmagazine.com False OK (200)

请注意计算字段 “Response” 现在体现的是原始的数值型响应码和对应的友好文本。

PowerShell 技能连载 - 测试网站的可用性

当一个网站不可用时,通常的问题是仅仅您不能访问该网站,还是其他所有人都不能访问。PowerShell 可以调用一个 Web Service 为您检查 web 站点的可用性。下面是一个简单的包装函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Test-Url
{
param
(
[Parameter(Mandatory,ValueFromPipeline)]
[string]
$Url
)

Add-Type -AssemblyName System.Web

$check = "https://isitdown.site/api/v3/"
$encoded = [System.Web.HttpUtility]::UrlEncode($url)
$callUrl = "$check$encoded"

Invoke-RestMethod -Uri $callUrl |
Select-Object -Property Host, IsItDown, Response_Code
}

它将调用一个 RESTful API 并且通过 URL 参数进行检查。这是为什么待测试的 URL 需要进行 URL 编码,这段代码调用 Invoke-RestMethod 并且以一个对象的形式接收测试结果。

1
2
3
4
5
PS C:\> Test-Url -Url powershellmagazine.com

host isitdown response_code
---- -------- -------------
powershellmagazine.com False 200

请注意这个示例中使用的 Web Service 是免费的,并且不需要注册或 API 密钥。缺点是该 Web Service 是限流的它可能会返回一个异常,提示您提交了太多请求。当这种情况发生时,只需要等待一阵子再重试。

PowerShell 技能连载 - 通过 Web Service 做单位转换

通过 PowerShell 访问 RESTful Web Service 十分容易:只需要将您的发送数据发给公开的 Web Service,并且接收结果即可。

以下是三个 PowerShell 函数,每个函数处理一个数字转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
function Convert-InchToCentimeter
{
param
(
[Parameter(Mandatory)]
[Double]
$Inch
)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$url = 'https://ucum.nlm.nih.gov/ucum-service/v1/ucumtransform/{0}/from/%5Bin_i%5D/to/cm' -f $Inch
$result = Invoke-RestMethod -Uri $url -UseBasicParsing
$result.UCUMWebServiceResponse.Response
}


function Convert-FootToMicrometer
{
param
(
[Parameter(Mandatory)]
[Double]
$Foot
)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$url = 'https://ucum.nlm.nih.gov/ucum-service/v1/ucumtransform/{0}/from/%5Bft_i%5D/to/um' -f $Foot
$result = Invoke-RestMethod -Uri $url -UseBasicParsing
$result.UCUMWebServiceResponse.Response
}


function Convert-GramToOunce
{
param
(
[Parameter(Mandatory)]
[Double]
$Gram
)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$url = 'https://ucum.nlm.nih.gov/ucum-service/v1/ucumtransform/{0}/from/g/to/%5Boz_ap%5D' -f $Gram
$result = Invoke-RestMethod -Uri $url -UseBasicParsing
$result.UCUMWebServiceResponse.Response
}

假设您有 Internet 连接,然后做单位换算就是一个函数调用那么简单了:

1
2
3
4
5
PS C:\> Convert-GramToOunce -Gram 230

SourceQuantity SourceUnit TargetUnit ResultQuantity
-------------- ---------- ---------- --------------
230.0 g [oz_ap] 7.3946717

需要注意的点有:

  • 您需要允许 Tls12 来允许 HTTPS 连接(参考代码)
  • 您需要遵守 Web Service 规定的规则,即当它需要整个数数时,您不能提交小数。

更多的转换功能请参考 https://ucum.nlm.nih.gov/ucum-service.html#conversion,您可以使用以上提供的函数作为模板来创建更多的转换函数。

PowerShell 技能连载 - 验证 Active Directory 凭据

PowerShell 可以通过 Active Directory 验证 AD 用户名和密码:

1
2
3
4
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$account = New-Object System.DirectoryServices.AccountManagement.PrincipalContext([DirectoryServices.AccountManagement.ContextType]::Domain, $env:userdomain)

$account.ValidateCredentials('user12', 'topSecret')

请注意这种方法只能作为诊断目的。它以明文的方式输入密码。

PowerShell 技能连载 - 为输出编号(第 1 部分)

如果你想增加一个递增的数字到你的输出,这里有一个简单的方法:

1
2
3
4
5
6
7
Get-Process |
Select-Object -Property '#', ProcessName, CPU -First 10 |
ForEach-Object -begin { $i = 0} -process {
$i++
$_.'#' = $i
$_
} -end {}

Select-Object 添加了一个名为 “#“ 的新属性,ForEach-Object 添加了一个自动递增的数字。结果如下:

 # ProcessName               CPU
 - -----------               ---
 1 AdobeCollabSync       65,5625
 2 AdobeCollabSync           0,5
 3 AGMService
...

PowerShell 技能连载 - 接受屏蔽的密码

如果曾经写过需要接受密码等敏感输入的 PowerShell 函数,请确保允许用户传入 SecureString。如果您通过明文接受密码,则存在很大的风险,即其他人可能在输入密码时看到密码,或者(更糟的是)密码已被记录,稍后可以在转储文件中找到。

下面是一个简单的框架,说明如何实现安全输入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Enter-Secret
{
param
(
[Parameter(Mandatory)]
[SecureString]
$SafeInput
)

$PlainText = [Management.Automation.PSCredential]::
new('x',$SafeInput).GetNetworkCredential().Password

"User entered $PlainText"

}

当用户运行 Enter-Secret,可以以屏蔽的方式输入密码。在内部,函数将安全字符串转换为纯文本。这样,秘密密码就永远不可见,也永远不会被记录下来。

从 SecureString 到 String 的转换是通过创建一个临时凭证对象来执行的。凭据对象有一个内置方法 (GetNetworkCredential()),用于将SecureString 转换为字符串。

PowerShell 技能连载 - 查找隐藏的 PowerShell 应用

最广为人知的 PowerShell 宿主当然是 PowerShell.exe 和 powershell_ise.exe,因为它们是开箱即用的。但是,运行 PowerShell 的宿主可能更多(而且是隐藏的)。任何实例化 PowerShell 引擎的软件都是一个 PowerShell 宿主。这可以是 Visual Studio Code(安装了 PowerShell扩展)、Visual Studio 或任何其它类似的软件。

要找出所有当前运行 PowerShell 的宿主,请运行以下命令:

1
2
3
4
5
6
7
8
Get-ChildItem -Path "\\.\pipe\" -Filter '*pshost*' |
ForEach-Object {
$id = $_.Name.Split('.')[2]
if ($id -ne $pid)
{
Get-Process -ID $id
}
}

结果看起来类似这样:

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
   1131     101   628520      42440             11216   0 SupportAssistAgent
   1011      82   269920     299208      85,30  17420   1 powershell_ise
    520      29    68012      75880       1,23  33532   1 powershell
    590      31    69508      77712       2,02  36636   1 powershell
    545      27    67952      76668       1,14  37584   1 powershell
   4114     654   801136     965032     129,69  28968   1 devenv

“SupportAssistAgent” 是由 Visual Studio Code 打开的,而 “devenv” 代表由 Visual Studio 启动的内部 PowerShell 宿主。

PowerShell 技能连载 - 控制处理器关联性

大多数现代计算机都有不止一个处理器,无论是物理处理器还是逻辑处理器。如果你想知道处理器的数量,这里有一段 PowerShell 代码:

1
2
Get-WmiObject -Class Win32_Processor |
Select-Object -Property Caption, NumberOfLogicalProcessors

结果看起来类似这样:

Caption                              NumberOfLogicalProcessors
-------                              -------------------------
Intel64 Family 6 Model 78 Stepping 3                         4

当您在这样的机器上运行一个进程时,它通常没有特定的处理器关联性,因此 Windows 决定该进程将运行在哪个处理器上。

如果愿意,可以为每个进程声明一个特定的处理器关联。这是很有用的,例如,如果你想控制一个程序可以使用的处理器,也就是防止一个进程使用所有的处理器。

处理器关联性由位标志控制。要找出当前处理器与进程的关联关系,请使用以下代码:

1
2
$process = Get-Process -Id $PID
[Convert]::ToString([int]$process.ProcessorAffinity, 2)

在本例中,用的是当前的 PowerShell 进程,但是您可以指定任何进程。典型的结果是:

1111

在 4 处理器的机器上,进程没有特定的关联性,可以使用任何处理器。要设置新的关联,最简单的方法是使用自己的位掩码并更改属性。例如,要将当前 PowerShell 进程仅锁定到第一个处理器,可以这样做:

1
2
3
4
5
6
7
# calculate the bit mask
$mask = '1000'
$bits = [Convert]::ToInt32($mask, 2)

# assign new affinity to current PowerShell process
$process = Get-Process -Id $PID
$process.ProcessorAffinity = $bits