PowerShell 技能连载 - 每日问候

以下是一个在 PowerShell 中接受一个字符串数组并返回一个随机的字符串,可以用作自定义问候语的简单方法:

1
2
3
4
5
6
7
8
$greetings =
'Hello there!',
'Glad to see you!',
'Happy coding!',
'Have a great day!',
'May the PowerShell be with you!'

$greetings | Get-Random

您所需要做的只是将这段代码加到您的 profile 脚本,例如这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# create profile if it does not yet exist
$exists = Test-Path -Path $Profile.CurrentUserAllHosts
if (!$exists)
{
$null = New-Item -Path $Profile.CurrentUserAllHosts -ItemType File -Force
}

# add code to profile
@'
$greetings =
'Hello there!',
'Glad to see you!',
'Happy coding!',
'Have a great day!',
'May the PowerShell be with you!'

$greetings | Get-Random
'@ | Add-Content -Path $Profile.CurrentUserAllHosts -Encoding Default

完成以后,PowerShell 将会使用自定义信息向您问候。

PowerShell 技能连载 - PowerShell 中 LINQ 的真实情况

不久前有一些关于 LINQ,一个 .NET 查询语言,在 PowerShell 中用来提升代码速度的报告。

直到 PowerShell 真正支持 Linq 之前,使用 Linq 是非常冗长的,并且需要使用强类型和没有文档的方法。另外,同样的事可以使用纯 PowerShell 方法来做,速度的提升很少——至少对 IPPro 相关的任务不明显。

以下是一个使用很简单的 Linq 语句对数字求和的测试用例。它接受 Windows 文件夹下的所有文件,然后对所有文件的长度求和:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$numbers = Get-ChildItem -Path $env:windir -File | Select-Object -ExpandProperty Length

(Measure-Command {
$sum1 = [Linq.Enumerable]::Sum([int[]]$numbers)
}).TotalMilliseconds

(Measure-Command {
$sum2 = ($numbers | Measure-Object -Sum).Sum
}).TotalMilliseconds

(Measure-Command {
$sum3 = 0
foreach ($number in $numbers) { $sum3+=$number }
}).TotalMilliseconds

当您运行它多次的时候,您会观察到执行时间的输出。Linq 的方法可以使用,但是对数据类型十分敏感。例如,您需要将数字数组转换为 integer 数组,否则 Linq 的 Sum() 方法将不起作用。

可以提炼出两条法则:

  1. 这时不值得使用 Linq,因为它尚未集成到 PowerShell 中,并且会产生难读的代码。它几乎相当于在 PowerShell 使用 C# 源代码。

  2. 如果您想提升速度,请在所有可能的地方避免使用管道。foreach 循环的执行速度比用管道将许多对象通过管道传到 ForEach-Object 快许多。

If Linq was better integrated into PowerShell in the future, it would indeed be highly interesting.

PowerShell 技能连载 - 神秘的 Windows 10 透明模式

When you open a native PowerShell console in Windows 10, you can hold down CTRL+SHIFT, then move your mouse wheel, to adjust console background color transparency, and let other windows shine through. The same works for cmd.exe as well, of course.
当您在 Windows 10 中打开一个原生的 PowerShell 控制台,按下 CTRL + SHIFT 键,然后滚动鼠标滚轮,就可以调节控制台背景色的透明度,并且让其它窗口的内容透射出来。当然这对 cmd.exe 也是有效的。

PowerShell 技能连载 - 清空 DNS 缓存

Windows 使用了 DNS 缓存技术,如果改变了 DNS 服务器,您需要刷新 DNS 缓存以使新的设置生效。PowerShell 对传统的控制台命令是有好的,所以只需要在 PowerShell 中运行这行代码:

1
PS> ipconfig /flushdns

PowerShell 技能连载 - 显示文件夹树

PowerShell 对旧的控制台命令是十分友好的,所以要显示文件夹的树形结构,使用旧的 “tree” 命令是十分简单的。它最好工作在一个原生的 PowerShell 控制台中,因为编辑器往往使用不同的字符集。请试试这个命令:

1
PS> Tree $home

请确保您是在一个原生的 PowerShell 控制台中或 VSCode 中运行这段代码。您还可以将结果通过管道输出到 clip.exe 并将它粘贴到一个文本文档中:

1
PS> Tree $home | clip.exe

PowerShell 技能连载 - 使用缓存的端口文件

在前一个技能中我们介绍了如何用 PowerShell 通过 IANA 下载端口分配信息。这个过程需要 Internet 连接并且需要一段时间。所以以下代码会查找缓存的 CSV 文件。如果缓存文件存在,端口信息会从离线文件中加载,否则将在线加载数据,并写入缓存文件。请特别注意如何使用 Tee-Object 命令创建缓存文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$url = 'https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.csv'
$CSVFile = "$env:temp\ports.csv"
$exists = Test-Path -Path $CSVFile

if (!$exists)
{
Write-Warning "Retrieving data online..."

$portinfo = Invoke-WebRequest -Uri $Url -UseBasicParsing | `
Select-Object -ExpandProperty Content | `
Tee-Object -FilePath $CSVFile | ConvertFrom-Csv
}
else
{
Write-Warning "Loading cached file..."
$portinfo = Import-Csv -Path $CSVFile
}

$portinfo | Out-GridView

PowerShell 技能连载 - 获取端口分配列表

IANA(互联网数字分配机构)维护了一个包含所有已知的端口分配的 CSV 文件。PowerShell 可以为您下载这个列表:

1
2
3
4
$out = "$env:temp\portlist.csv"
$url = 'https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.csv'
$web = Invoke-WebRequest -Uri $url -UseBasicParsing -OutFile $out
Import-Csv -Path $out -Encoding UTF8

返回的结果是一个包含所有的端口分配的面向对象格式的列表。接下来您可以使用这个信息例如过滤特定的端口:

1
2
3
Import-Csv -Path $out -Encoding UTF8 |
Where-Object 'transport protocol' -eq 'tcp' |
Where-Object 'Port Number' -lt 1000

PowerShell 技能连载 - 世界时钟

PowerShell 提供了 Get-TimeZone 命令,能返回所有定义过的时区和它们的时差。以下是列出世界时钟相关的代码:

1
2
3
4
5
6
7
8
9
10
11
12
$isSummer = (Get-Date).IsDaylightSavingTime()


Get-TimeZone -ListAvailable | ForEach-Object {
$dateTime = [DateTime]::UtcNow + $_.BaseUtcOffset
$cities = $_.DisplayName.Split(')')[-1].Trim()
if ($isSummer -and $_.SupportsDaylightSavingTime)
{
$dateTime = $dateTime.AddHours(1)
}
'{0,-30}: {1:HH:mm"h"} ({2})' -f $_.Id, $dateTime, $cities
}

结果类似如下:

Dateline Standard Time         : 18:41h ()
UTC-11                         : 19:41h (Coordinated Universal Time-11)
Aleutian Standard Time         : 21:41h (Aleutian Islands)
Hawaiian Standard Time         : 20:41h (Hawaii)
Marquesas Standard Time        : 21:11h (Marquesas Islands)
Alaskan Standard Time          : 22:41h (Alaska)
UTC-09                         : 21:41h (Coordinated Universal Time-09)
Pacific Standard Time (Mexico) : 23:41h (Baja California)
UTC-08                         : 22:41h (Coordinated Universal Time-08)
Pacific Standard Time          : 23:41h ()
US Mountain Standard Time      : 23:41h (Arizona)
Mountain Standard Time (Mexico): 00:41h (Chihuahua, La Paz, Mazatlan)
Mountain Standard Time         : 00:41h ()
Central America Standard Time  : 00:41h (Central America)
Central Standard Time          : 01:41h ()
Easter Island Standard Time    : 01:41h (Easter Island)
Central Standard Time (Mexico) : 01:41h (Guadalajara, Mexico City, Monterrey)
Canada Central Standard Time   : 00:41h (Saskatchewan)
SA Pacific Standard Time       : 01:41h (Bogota, Lima, Quito, Rio Branco)
Eastern Standard Time (Mexico) : 02:41h (Chetumal)
Eastern Standard Time          : 02:41h ()

PowerShell 技能连载 - 设置时区

只有拥有了管理员特权才可以修改电脑的时间和日期,但任何用户都可以修改时区,例如当您在旅游时。PowerShell 5 提供了一系列非常简单的 cmdlet 来管理时区。首先,检查您的当前设置:

1
2
3
4
5
6
7
8
9
PS> Get-TimeZone


Id : W. Europe Standard Time
DisplayName : (UTC+01:00) Amsterdam, Berlin, Bern, Rom, Stockholm, Wien
StandardName : Mitteleuropäische Zeit
DaylightName : Mitteleuropäische Sommerzeit
BaseUtcOffset : 01:00:00
SupportsDaylightSavingTime : True

下一步,尝试修改时区。以下代码打开一个包含所有可用时区的窗口:

1
PS> Get-TimeZone -ListAvailable | Out-GridView

当您知道您希望设置的时区的正式 ID 后,请使用 Set-TimeZone 命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
PS> Set-TimeZone -Id 'Chatham Islands Standard Time'

PS> Get-Date

Samstag, 27. Mai 2017 18:32:53



PS> Set-TimeZone -Id 'W. Europe Standard Time'

PS> Get-Date

Samstag, 27. Mai 2017 07:48:02