PowerShell 技能连载 - 读取新闻订阅

以下是一个针对有德语技能的用户的特殊服务——对于其他人修改代码会有所挑战:以下代码使用了德国主要新闻杂志的 RSS 订阅,打开一个选择窗口。在窗口中您可以选择一篇或多篇文章,然后在缺省的浏览器中打开选择的文章:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# URL to RSS Feed
$url = 'http://www.spiegel.de/schlagzeilen/index.rss'


$xml = New-Object -TypeName XML
$xml.Load($url)

# the subproperties (rss.channel.item) depend on the RSS feed you use
# and may be named differently
$xml.rss.channel.item |
Select-Object -Property title, link |
Out-GridView -Title 'What would you like to read today?' -OutputMode Multiple |
ForEach-Object {
Start-Process $_.link
}

基本的设计过程是一致的:要将代码改为另一个 RSS 订阅,只需要导航到相应的属性(背后的 XML 的嵌套结构)。

PowerShell 技能连载 - 在资源管理器中启用预览 PowerShell 文件

当您在 Windows 的资源管理器中打开预览窗格查看 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
function Enable-PowerShellFilePreview
{
[CmdletBinding()]
param
(
[string]
$Font = 'Courier New',

[int]
$FontSize = 60
)

# set the font and size (also applies to Notepad)
$path = "HKCU:\Software\Microsoft\Notepad"
Set-ItemProperty -Path $path -Name lfFaceName -Value $Font
Set-ItemProperty -Path $path -Name iPointSize -Value $FontSize

# enable the preview of PowerShell files
$path = 'HKCU:\Software\Classes\.ps1'
$exists = Test-Path -Path $path
if (!$exists){
$null = New-Item -Path $Path
}
$path = 'HKCU:\Software\Classes\.psd1'
$exists = Test-Path -Path $path
if (!$exists){
$null = New-Item -Path $Path
}

$path = 'HKCU:\Software\Classes\.psm1'
$exists = Test-Path -Path $path
if (!$exists){
$null = New-Item -Path $Path
}


Get-Item HKCU:\Software\Classes\* -Include .ps1,.psm1,.psd1 | Set-ItemProperty -Name PerceivedType -Value text
}

运行这个函数后,使用这个命令:

1
PS> Enable-PowerShellFilePreview

如果您喜欢的话,还可以改变预览的字体系列和字号。请注意该设置和记事本共享:

1
PS> Enable-PowerShellFilePreview -Font Consolas -FontSize 100

不需要重启系统就可以生效。只需要确保 Windows 资源管理器的预览窗格可见,并选取一个 PowerShell 文件。

PowerShell 技能连载 - 移除空的数组元素(第 2 部分)

如果您想彻底移除空的数组元素(而不需要关心任何空属性),以下是一些性能根本不同的几种实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
# create huge array with empty elements
$array = 1,2,3,$null,5,0,3,1,$null,'',3,0,1
$array = $array * 1000

# "traditional" approach (6 sec)
Measure-Command {
$newArray2 = $array | Where-Object { ![string]::IsNullOrWhiteSpace($_) }
}

# smart approach (0.03 sec)
Measure-Command {
$newArray3 = foreach ($_ in $array) { if (![String]::IsNullOrWhiteSpace($_)){ $_} }
}

PowerShell 技能连载 - 移除空的数组元素(第 1 部分)

有些时候您会遇到包含空元素的列表(数组)。那么移除空元素的最佳方法是?

让我们首先关注一个普遍的场景:以下代码从注册表读取已安装的软件并创建一个软件清单。该软件清单将显示在一个网格视图窗口中,而很可能能看到包含空属性的元素:

1
2
3
4
5
6
7
8
9
$Paths = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKCU:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'

$software = Get-ItemProperty -Path $paths -ErrorAction Ignore |
Select-Object -Property DisplayName, DisplayVersion, UninstallString

$software | Out-GridView

让我们忽略所有显示名称为空的元素:

1
2
# remove elements with empty DisplayName property
$software = $software | Where-Object { [string]::IsNullOrWhiteSpace($_.DisplayName)}

由于空属性既包含“真正”为空 ($null) 也包含空字符串 (''),您需要检查它们两者。更简单的方法是将它们隐式转换为 Boolean。然而,这样做仍然会移除数值 0:

1
2
# remove elements with empty DisplayName property
$software = $software | Where-Object { $_.DisplayName }

使用 PowerShell 3 引入的简化语法,您甚至可以这样写:

1
2
# remove elements with empty DisplayName property
$software = $software | Where-Object DisplayName

如果你想节省几毫秒,请使用 where 方法:

1
2
# remove elements with empty DisplayName property
$software = $software.Where{ $_.DisplayName }

如果您想处理一个大数组,用 foreach 循环更有效(效率提升 15 倍):

1
2
# remove elements with empty DisplayName property
$software = foreach ($_ in $software){ if($_.DisplayName) { $_ }}

PowerShell 技能连载 - “危险的”比较

假设您希望排除某个数组中所有为空字符串或者 null 元素。以下是许多人可能的做法:

1
2
3
4
5
6
7
8
PS> 1,2,$null,"test","",9 | Where-Object { $_ -ne '' -and $_ -ne $null }

1
2
test
9

PS>

然而,这个对比是危险的,因为它也排除了数值 0:

1
2
3
4
5
6
7
8
PS> 1,2,0,$null,"test","",0,9 | Where-Object { $_ -ne '' -and $_ -ne $null }

1
2
test
9

PS>

PowerShell 过滤掉了数值 0,因为它等同于一个空字符串:

1
2
3
4
5
PS> 0 -eq ''
True

PS> 1 -eq ''
False

这是因为在比较时,以等号左侧的数据类型为准,而由于左侧是一个 integer 值,所以 PowerShell 将空字符串也转换成一个 integer,而转换的结果值是 0。

为了安全地进行比较,请记住一定将相关的数据类型放在等号左侧,而不是右侧:

1
2
3
4
5
6
7
8
9
10
PS> 1,2,0,$null,"test","",0,9 | Where-Object { '' -ne $_ -and $null -ne $_ }

1
2
0
test
0
9

PS>

或者更好一点,使用 API 函数来确认空值:

1
2
3
4
5
6
7
8
9
10
PS> 1,2,0,$null,"test","",0,9 | Where-Object { ![string]::IsNullOrWhiteSpace($_) }

1
2
0
test
0
9

PS>

PowerShell 技能连载 - 计算一个月的第一天和最后一天

对于报表以及类似的场景,脚本可能需要获得指定月份的第一天和最后一天。第一天很简单,但最后一天依赖于月份和年份。以下是一个简单的计算器。只需要指定您需要的月份和年份:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[ValidateRange(1,12)][int]$month = 3
$year = 2019
$last = [DateTime]::DaysInMonth($year, $month)
$first = Get-Date -Day 1 -Month $month -Year $year -Hour 0 -Minute 0 -Second 0
$last = Get-Date -Day $last -Month $month -Year $year -Hour 23 -Minute 59 -Second 59



PS> $first
3/1/2019 12:00:00 AM

PS> $last
3/31/2019 11:59:59 PM

PS>

PowerShell 技能连载 - 格式化 DateTime

当您拥有一个真正的 DateTime 对象(比如不是字符串)时,您就拥有了许多强大的格式化功能。您可以直接获取一个 DateTime 对象:

1
2
3
4
PS> $installDate = (Get-CimInstance -Class Win32_OperatingSystem).InstallDate

PS> $installDate.GetType().FullName
System.DateTime

或者您可以将一个字符串转换为一个 DateTime 对象:

1
2
3
4
PS> $psconf = Get-Date -Date '2019-06-04 09:00'

PS> $psconf.GetType().FullName
System.DateTime

当您拥有一个 DateTime 对象时,请使用 ToString() 方法并提供一个或两个参数。

第一个参数决定您希望使用日期的哪些部分,并使用这些占位符(大小写敏感!):

y       Year
M       Month
d       Day
H       Hour
m       Minute
s       Second
f       Millisecond

指定了越多占位符,就可以得到越多的细节:

1
2
3
4
5
6
7
8
9
10
PS> (Get-Date).ToString('dd')
30

PS> (Get-Date).ToString('ddd')
So

PS> (Get-Date).ToString('dddd')
Sonntag

PS>

(如您所见,PowerShell 使用的是缺省的语言,这个例子中使用的是德语)

要以 ISO 格式输出一个 DateTime,请使用这段代码:

1
2
3
4
5
6
PS> $installDate = (Get-CimInstance -Class Win32_OperatingSystem).InstallDate

PS> $installDate.ToString('yyyy-MM-dd HH:mm:ss')
2018-06-08 18:24:46

PS>

如果您也希望指定区域设置(语言),请在第二个参数指定 CultureInfo

1
2
3
4
5
6
7
8
9
10
11
12
13
PS> (Get-Date).ToString('dddd', [System.Globalization.CultureInfo]'en-us')
Sunday

PS> (Get-Date).ToString('dddd', [System.Globalization.CultureInfo]'zh')
星期日

PS> (Get-Date).ToString('dddd', [System.Globalization.CultureInfo]'es')
domingo

PS> (Get-Date).ToString('dddd', [System.Globalization.CultureInfo]'fr')
dimanche

PS>

如果您想了解某个区域设置的区域代码,请试试这段代码:

1
PS> [System.Globalization.CultureInfo]::GetCultures('Installed') | Out-GridView -PassThru

PowerShell 技能连载 - 解析 Windows 安装日期

是否关心过您的 Windows 已经安装了多久?一个单行的代码可以告诉您结果:

1
2
3
PS> (Get-CimInstance -Class Win32_OperatingSystem).InstallDate

Freitag, 8. Juni 2018 18:24:46

有两件事值得注意:第一,我们显然在使用德文的系统。第二,安装的日期可能比您想象的更近:每个新的 Windows 10 主版本更新实际上导致了一个完整的重新安装过程。

如果您希望改变 DateTime 输出的语言,只需要使用 ToString() 和一个 CultureInfo 对象:

1
2
3
4
PS> (Get-CimInstance -Class Win32_OperatingSystem).InstallDate.ToString([System.Globalization.CultureInfo]'en-us')
6/8/2018 6:24:46 PM

PS>

如果您想了解 Windows 安装了多少填,请使用 New-TimeSpan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PS> New-TimeSpan -Start (Get-CimInstance -Class Win32_OperatingSystem).InstallDate


Days : 204
Hours : 18
Minutes : 53
Seconds : 52
Milliseconds : 313
Ticks : 176936323133869
TotalDays : 204,787411034571
TotalHours : 4914,89786482969
TotalMinutes : 294893,871889782
TotalSeconds : 17693632,3133869
TotalMilliseconds : 17693632313,3869


PS> (New-TimeSpan -Start (Get-CimInstance -Class Win32_OperatingSystem).InstallDate).TotalDays
204,78764150864

PS> (New-TimeSpan -Start (Get-CimInstance -Class Win32_OperatingSystem).InstallDate).Days
204

PowerShell 技能连载 - 在文件管理器中隐藏 OneDrive

是否厌倦了 OneDrive 图表污染了您的文件管理器树形视图?如果您不使用 OneDrive,那么有两个很好用的函数可以在文件管理器里隐藏或显示 OneDrive 图标:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Disable-OneDrive
{
$regkey1 = 'Registry::HKEY_CLASSES_ROOT\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}'
$regkey2 = 'Registry::HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}'
Set-ItemProperty -Path $regkey1, $regkey2 -Name System.IsPinnedToNameSpaceTree -Value 0
}


function Enable-OneDrive
{
$regkey1 = 'Registry::HKEY_CLASSES_ROOT\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}'
$regkey2 = 'Registry::HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}'
Set-ItemProperty -Path $regkey1, $regkey2 -Name System.IsPinnedToNameSpaceTree -Value 1
}

PowerShell 技能连载 - 将 Windows 服务器转变为工作站

PowerShell 5 及以上版本提供了一个自动添加 Windows 功能的 cmdlet,所以如果您正在运行 Windows Server 并且想使用 Workstation 功能,请以管理员权限打开一个 PowerShell,然后运行以下代码:

1
Enable-WindowsOptionalFeature -FeatureName DesktopExperience -All -Online -NoRestart
PowerShell 技术 QQ 群