PowerShell 技能连载 - 高效统计文件数量(第一部分)

一个快速但浪费的统计文件数量的方法如下:

1
(Get-ChildItem -Path c:\windows).Count

但是,这将产生一些内存负担,因为在 Count 属性能够获取对象数量之前,所有文件将会堆在内存里。当进行递归搜索时,这种情况更严重。

一个节约非常多资源的方法是类似这样使用 Measure-Object

1
(Get-ChildItem -Path c:\windows | Measure-Object).Count

这里使用流来获取项目的数量,所以 PowerShell 不需要在内存中存储所有文件。

PowerShell 技能连载 - 还原 TinyUrl 的真实地址

URL 缩短功能对于 Twitter 消息十分有用,但是隐藏了真实地址。您是否真的信任 http://bit.ly/e0Mw9w 呢?

以下是一个简单的方法,帮您还原缩短后的 URL 指向的真实地址:

1
2
3
4
5
6
$shortUrl = "http://bit.ly/e0Mw9w"

$longURL = Invoke-WebRequest -Uri "http://untiny.me/api/1.0/extract?url=$shortUrl&format=text" -UseBasicParsing |
Select-Object -ExpandProperty Content

"'$shortUrl' -> '$longUrl'"

如您所见,这个例子中缩短的 URL 指向的是 Lee Holmes 的博客:http://www.leeholmes.com/projects/ps_html5/Invoke-PSHtml5.ps1 。Lee Holmes 是一个 PowerShell 团队成员,如果您信任他,那么可以好奇地运行他著名的代码片段:

1
iex (New-Object Net.WebClient).DownloadString("http://bit.ly/e0Mw9w")

这是一个能说明 Invoke-Expression 别名为“iex”有危险的很好例子。

PowerShell 技能连载 - 创建彩色的天气报告

在前一个技能中我们介绍了如何用 Invoke-WebRequest 来获取天气预报数据。获取到的数据是纯黑白的文本。

要获取彩色的报告,Windows 10 可以利用 powershell.exe 的控制序列特性。只需要在 PowerShell 控制台中运行以下代码:

1
2
3
$City = 'Hannover'

(Invoke-WebRequest "http://wttr.in/$City" -UserAgent curl).content -split "`n"

只需要将 user agent 设为“curl”,Windows 10 powershell.exe 就能收到包含色彩控制序列的输出。

PowerShell 技能连载 -获取天气预报

Invoke-WebRequest 可以轻松地获得网页内容。如果不指定 –UseBasicParsing 参数,HTML 内容会被解析成 Internet Explorer DOM。通过这种方法,PowerShell 只需要几行代码就可以获取世界上大多数城市当前的天气预报:

1
2
3
4
5
6
$City = 'New York'
$weather = Invoke-WebRequest -Uri "http://wttr.in/$City"
$text = $weather.ParsedHtml.building.outerText

$text
$text | Out-GridView

要注意一些事情:

  • Out-GridView 只能显示有限行的文本。要查看完整的报告,请将 $text 输出到控制台。
  • Invoke-WebRequest 需要内置的 Windows 浏览器运行和初始化至少一次
  • 以上代码是脆弱的:一旦网站作者对页面内容重新布局,脚本可能就不能工作了

PowerShell 技能连载 - 避免使用 Read-Host

您是否使用 Read-Host 来接受用户的输入?如果您是这么做的,请重新考虑一下。Read-Host 总是提示用户,并且用了 Read-Host 就无法自动化运行脚本:

1
$City = Read-Host -Prompt 'Enter City'

一个更好的简单方法是这样:

1
2
3
4
param
(
[Parameter(Mandatory)]$City
)

它创建了一个必选参数。如果用户没有提供传输这个实参,那么会创建一个提示,效果类似 Read-Host。但是,用户始终可以传入这个脚本的参数并使脚本自动化运行。只需要确保只有一个 param() 语句,并且必须放在脚本的开始处。逗号来分隔多个参数。

如果您不喜欢 param() 创建的提示,那么可以用这种方法:

1
2
3
4
param
(
$City = (Read-Host -Prompt 'Enter City')
)

这样,您可以完全控制提示的内容,并且用户又可以通过实参传入参数,并且无人值守运行脚本。

PowerShell 技能连载 - 使用在线帮助

PowerShell 的发行版并没有带帮助文件,而本地安装帮助文件需要管理员权限。

更简单的获取帮助的方法是访问 cmdlet 的在线帮助,类似这样:

1
Get-Help -Name Get-Acl -Online

这将启动一个浏览器并导航到在线帮助。在线帮助通常内容更新并且更容易获得。当然,它需要 internet 连接,并且并不是每个 cmdlet 都有在线帮助版本。

PowerShell 技能连载 - 同时输出和赋值

在前一个技能中我们介绍了如何记录脚本结果,以及如何使用括号来同时输出和赋值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PS> ($a = Get-Process -Id $pid)

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1595 102 283200 325444 64,56 6436 1 powershell_ise



PS> $a

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1595 102 283200 325444 64,75 6436 1 powershell_ise



PS>

还可以用 -OutVariable 通用参数来实现相同的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PS> Get-Process -Id $pid -OutVariable b

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1731 105 290336 341688 66,66 6436 1 powershell_ise



PS> $b

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1731 105 290336 341688 66,92 6436 1 powershell_ise



PS>

Tee-Object 是第三种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PS> Get-Process -Id $pid | Tee-Object -Variable c

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1759 109 292300 343644 71,53 6436 1 powershell_ise



PS> $c

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1759 109 292300 343644 71,69 6436 1 powershell_ise



PS>

以上方法使用了管道,而管道的速度比较慢。如果希望提升性能,那么避免使用管道:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PS> Tee-Object -InputObject (Get-Process -Id $pid) -Variable d

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1761 111 294568 345268 74,31 6436 1 powershell_ise



PS> $d

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1761 111 294568 345268 74,59 6436 1 powershell_ise



PS>

PowerShell 技能连载 - 记录脚本输出

有一系列办法能记录脚本的输出结果,但是一个非常偷懒的办法是使用 Start-Transcript。在 PowerShell 5 中,这个 cmdlet 不仅在 powershell.exe 中支持,而且在所有宿主中都支持。所以您在 PowerShell ISE 或其它编辑器中都可以使用它。另外,transcript 支持嵌套,所以如果您写一个脚本,您可以安全地在起始处加上 Start-Transcript 并且在结尾处加上 Stop-Transcript

Start-Transcript 将所有输出输出写入一个文本文件。如果您没有指定路径,那么该 cmdlet 将会使用默认路径。您只需要确保脚本确实产生了可记录的输出。

要使脚本更详细,请结合这个技巧一起使用:当您将赋值语句放入一对括号 (),该赋值语句也会输出赋值的数据。这个输出结果也会被 transcript 接收到。请试试一下代码:

1
2
3
4
5
6
7
# default assignment, no output
$a = Get-Service
$a.Count

# assignment in parentheses, outputs the assignment
($b = Get-Service)
$b.Count

PowerShell 技能连载 - 在 Linux 的 PowerShell Core 中安装模块

当您想通过 PowerShellGet 库为所有用户安装模块,您需要管理员权限。在 Linux 的 PowerShell Core 中,您可以用 sudo 命令来启用管理员权限,并且运行 PowerShell。只需要把命令写在在大括号中即可。

在 Linux 的 PowerShell Core 上,以下命令将为所有用户从 PowerShell Gallery 中安装 AzureRM.NetCore:

1
sudo powershell -Command {Install-Module -Name AzureRM.Netcore}

PowerShell 技能连载 - PowerShell.exe 的“大括号秘密”

当从 PowerShell 或 PowerShell Core 中调用 powershell.exe 时有一些小秘密:当您执行 powershell.exe 并通过 -Command 传递一些命令时,PowerShell 将运行这个命令并返回纯文本:

1
2
3
$a = powershell -noprofile -Command Get-Service
$a[0].GetType().FullName
System.String

所以当您将代码放在大括号中执行时,PowerShell 会将强类型的结果序列化后返回:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$a = powershell -noprofile -Command { Get-Service }
$a[0].GetType().FullName
System.Management.Automation.PSObject

$a[0] | Select-Object -Property *

Name : AdobeARMservice
RequiredServices : {}
CanPauseAndContinue : False
CanShutdown : False
CanStop : True
DisplayName : Adobe Acrobat Update Service
DependentServices : {}
MachineName : .
ServiceName : AdobeARMservice
ServicesDependedOn : {}
Status : Running
ServiceType : Win32OwnProcess
StartType : Automatic
Site :
Container :