PowerShell 技能连载 - 检测电源状态

虽然很难直接访问 Windows 电源管理 API,但还有其他 API 可以实现相同需求。以下代码返回您机器的当前电源状态。如果您使用的是笔记本电脑并且没有交流电源,它可以告诉您剩余电池电量:

1
2
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SystemInformation]::PowerStatus

结果类似这样:

PowerLineStatus      : Offline
BatteryChargeStatus  : 0
BatteryFullLifetime  : -1
BatteryLifePercent   : 0,45
BatteryLifeRemaining : 13498

PowerShell 技能连载 - 检测计划外的关机

如果 Windows 崩溃或意外停止,当下次重启时,它会产生一条 ID 为 41 的内核错误日志。如果您想检查回顾您的 Windows 是否正常关闭,请尝试以下代码:

1
2
3
Get-EventLog -Logname System -Source "Microsoft-Windows-Kernel-Power" |
Where-Object EventID -eq 41 |
Select-Object Index,TimeWritten,Source,EventID

一种更现代且与 PowerShell 7 兼容的方式是使用 Get-WinEvent,而不是使用过滤器哈希表:

1
2
3
4
5
Get-WinEvent -FilterHashtable @{
LogName = 'System'
ProviderName = 'Microsoft-Windows-Kernel-Power'
Id = 41
}

PowerShell 技能连载 - 奇怪的 Cmdlet:New-TemporaryFile

这是一个 PowerShell(和 Windows PowerShell)中比较隐藏的 cmdlet:New-TemporaryFile。看到这样一个相对无用的 cmdlet 成为 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
# load module that defines the function:
PS C:\> New-TemporaryFile -WhatIf
What if: Performing the operation "New-TemporaryFile" on target "C:\Users\tobia\AppData\Local\Temp".

# dump function source code:
PS C:\> ${function:New-TemporaryFile}

[CmdletBinding(
HelpURI='https://go.microsoft.com/fwlink/?LinkId=526726',
SupportsShouldProcess=$true)]
[OutputType([System.IO.FileInfo])]
Param()

Begin
{
try
{
if($PSCmdlet.ShouldProcess($env:TEMP))
{
$tempFilePath = [System.IO.Path]::GetTempFileName()
}
}
catch
{
$errorRecord = [System.Management.Automation.ErrorRecord]::new($_.Exception,"NewTemporaryFileWriteError", "WriteError", $env:TEMP)
Write-Error -ErrorRecord $errorRecord
return
}

if($tempFilePath)
{
Get-Item $tempFilePath
}
}

它的核心是这样的:

1
2
PS> [System.IO.Path]::GetTempFileName()
C:\Users\tobia\AppData\Local\Temp\tmp671.tmp
方法具有误导性,因为它实际上在您每次调用它时都会创建一个新的临时文件。`New-TemporaryFile` 返回的是临时文件对象,而不是字符串路径,这更好地说明了上面的问题。它的本质上是这样的:
1
2
3
4
5
6
7
8
9
10
11

```powershell
PS C:\> Get-Item ([System.IO.Path]::GetTempFileName())


Directory: C:\Users\tobia\AppData\Local\Temp


Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 26.01.2022 12:48 0 tmpC6DF.tmp

PowerShell 技能连载 - 本地化日期和时间标签(第 2 部分)

在上一个技能中,我们解释了如何查看所有受支持的 Windows 文化并让 Windows 翻译工作日名称。让我们玩得更开心一些,翻译月份名称。

这是一种特别简单的方法,可以确认您想要使用的文化的简称:

1
2
3
4
[System.Globalization.CultureInfo]::GetCultures('AllCultures') |
Where-Object Name |
Select-Object -Property Name, DisplayName |
Out-GridView -Title 'Select Culture' -OutputMode Single

这将打开一个包含所有支持的文化的网格视图窗口。使用位于其顶部的空文本框来过滤文化,然后选择一个并单击右下角的确定。您需要的是要使用的文化的简称。例如,要使用俄罗斯文化,简称为 “ru”。

现在,在以下调用中替换选定的文化名称:

1
2
3
4
5
6
7
8
9
10
11
12
13
PS> [System.Globalization.CultureInfo]::GetCultureInfo( 'ru' ).DateTimeFormat.MonthNames
Январь
Февраль
Март
Апрель
Май
Июнь
Июль
Август
Сентябрь
Октябрь
Ноябрь
Декабрь

同样,您可以调整我们之前技巧中的代码来创建两种语言的翻译表:

1
2
3
4
5
6
7
8
9
10
11
12
$english = [System.Globalization.CultureInfo]::GetCultureInfo( 'en' ).DateTimeFormat.MonthNames
$russian = [System.Globalization.CultureInfo]::GetCultureInfo( 'ru' ).DateTimeFormat.MonthNames

for($x=0; $x-lt 12; $x++)
{

[PSCustomObject]@{
Id = $x+1
English = $english[$x]
Russian = $russian[$x]
}
}

结果类似这样:

Id English   Russian
-- -------   -------
 1 January   Январь
 2 February  Февраль
 3 March     Март
 4 April     Апрель
 5 May       Май
 6 June      Июнь
 7 July      Июль
 8 August    Август
 9 September Сентябрь
10 October   Октябрь
11 November  Ноябрь
12 December  Декабрь

PowerShell 技能连载 - 本地化日期和时间标签(第 1 部分)

Windows 内置了对各种文化的支持。以下是支持的文化列表及其简称:

1
2
3
4
5
6
7
8
9
10
11
12
PS> [System.Globalization.CultureInfo]::GetCultures('AllCultures') | Select-Object -Property Name, DisplayName

Name DisplayName
---- -----------
aa Afar
aa-DJ Afar (Djibouti)
aa-ER Afar (Eritrea)
aa-ET Afar (Ethiopia)
af Afrikaans
af-NA Afrikaans (Namibia)
af-ZA Afrikaans (South Africa)
...

它还带有完全翻译的日期和时间组件表达。如果您想知道 Kikuyu(肯尼亚)中使用的工作日名称,请查找适当的文化名称( “ki”),然后尝试以下操作:

1
2
3
4
5
6
7
8
PS> [System.Globalization.CultureInfo]::GetCultureInfo( 'ki' ).DateTimeFormat.DayNames
Kiumia
Njumatatu
Njumaine
Njumatana
Aramithi
Njumaa
Njumamothi

你甚至可以为多种语言创建一个“翻译表”,因为你在 DayNames 中看到的是一个带有数字索引的数组:

1
2
PS> [System.Globalization.CultureInfo]::GetCultureInfo( 'ki' ).DateTimeFormat.DayNames[0]
Kiumia

这是一个显示英文和中文日期名称的翻译表:

1
2
3
4
5
6
7
8
9
10
11
$english = [System.Globalization.CultureInfo]::GetCultureInfo( 'en' ).DateTimeFormat.DayNames
$chinese = [System.Globalization.CultureInfo]::GetCultureInfo( 'zh' ).DateTimeFormat.DayNames

for($x=0; $x-lt7; $x++)
{

[PSCustomObject]@{
English = $english[$x]
Chinese = $chinese[$x]
}
}

结果如下所示:

English   Chinese
-------   -------
Sunday    星期日
Monday    星期一
Tuesday   星期二
Wednesday 星期三
Thursday  星期四
Friday    星期五
Saturday  星期六

PowerShell 技能连载 - 计算第几周(第 2 部分)

在上一个技能中,我们解释了如何计算给定日期的日历周。如您所见,这取决于文化和日历设置,并且可能因文化而异。

这就是为什么还有 “ISOWeek” 的原因:它遵守 ISO 8601 并且是标准化的。不幸的是,.NET 中的经典 API 并不总是能计算出正确的 ISOWeek。

这就是为什么微软在 .NET Standard(PowerShell 7 使用的可移植 .NET)和 .NET Framework 5 中添加了一个名为 “ISOWeek” 的全新类。

下面这行代码返回任何日期的 ISOWeek(当在 PowerShell 7 中运行时):

1
2
PS> [System.Globalization.ISOWeek]::GetWeekOfYear('2022-01-01')
52

在 Windows PowerShell 中运行时,同样的代码会返回红色的异常,因为 Windows PowerShell 基于完整的 .NET Framework,而在当前版本中尚不支持此 API。

PowerShell 技能连载 - 计算第几周(第 1 部分)

计算第几周不是一件很容易的事,并且根据文化不同而不同。以下是一个计算任何日期是第几周的方法:

1
2
3
4
5
6
7
8
9
10
# calculate day of week
# adjust calendar specs to your culture

$Date = [DateTime]'2021-12-31'
$CalendarWeekRule = [System.Globalization.CalendarWeekRule]::FirstDay
$FirstDayOfWeek = [System.DayOfWeek]::Monday

$week = [System.Globalization.DateTimeFormatInfo]::CurrentInfo.Calendar.GetWeekOfYear( $date, $calendarWeekRule, $firstDayOfWeek )

"$date = week $week"

只需确保您按照当地文化调整了日历的周规则和一周的第一天。

前面的示例使用当前的文化日历。如果您想控制文化,请尝试使用这种方法:

1
2
3
4
5
6
7
8
9
10
$Date = [DateTime]'2022-12-31'
$CultureName = 'de-de'
$CalendarWeekRule = [System.Globalization.CalendarWeekRule]::FirstDay
$FirstDayOfWeek = [System.DayOfWeek]::Monday

$culture = [System.Globalization.CultureInfo]::GetCultureInfo($CultureName)
$week = $culture.Calendar.GetWeekOfYear($Date, $CalendarWeekRule, $FirstDayOfWeek)


"$Date = week $week"

在这里,您可以使用 $CultureName 来定义要使用的日历的文化名称。

PowerShell 技能连载 - 通过 PowerShell 创建日历电子表格

是否需要计划为您的俱乐部,社区或爱好进行重复的会议吗?当然,有很多在线工具可以帮助您,但如果您想在 Microsoft Excel 中创建日历列表,PowerShell 可以是一个优秀的帮手。

让我们假设您每周三都有一次重复的会议,会议在下午十二点开始,除了每个月的最后一周。

您可以这样使用 PowerShell,而不是将这些日期和时间手动添加到 Excel 表:

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
44
45
# generate calendar for weekly incidents

$startdate = [DateTime]'2022-06-01'
$numberOfWeeks = 52
$result = for ($week = 0; $week -lt $numberOfWeeks; $week ++)
{
# calculate the real date each week
$realdate = $startdate + (New-Timespan -days (7*$week))

# calculate the current month
$month = $realdate.Month

# calculate the days in this month
$daysInMonth = [DateTime]::DaysInMonth($realdate.Year, $realdate.Month)

# make arbitrary adjustments, i.e. set start time to 12PM by default, but 7PM on the last week of a month

# are we in the last week of a month?
if ($realdate.Day -gt ($daysInMonth-7))
{
# add 19 hours
$realdate = $realdate.AddHours(19)
}
else
{
# add 12 hours
$realdate = $realdate.AddHours(12)
}

# create your Excel sheet layout as a CSV file
[PSCustomObject]@{
Start = $realdate
IsOnline = $false
Title = ''
Speaker = ''
Notes = ''
}
}


$path = "$env:temp\calendar.csv"
$result | Export-Csv -UseCulture -Path $path -Encoding UTF8 -NoTypeInformation

# open CSV in Excel
Start-Process -FilePath excel -ArgumentList $path

此脚本使用了许多有用的技术:

  • 在循环中使用偏移量来构建日期(在此示例中是 7 天,可以轻松调整成任何其他间隔)
  • 通过计算当前月份的天数来识别“该月的最后一周”,然后根据此计算日期进行调整
  • 在 Microsoft Excel 中生成 CSV 数据和打开 CSV(如果已安装)

PowerShell 技能连载 - 打开关闭 Windows 的对话框

以下是打开关闭 Windows 对话框的一行代码:

1
(New-Object -ComObject Shell.Application).ShutdownWindows()

使用此行代码,它变成了名为 “bye” 的新命令:

1
function bye { (New-Object -ComObject Shell.Application).ShutdownWindows() }

如果将此行放在 $profile 中的自动配置文件 (start) 脚本中(可能需要先创建该文件),则完成脚本时,您现在可以简单地输入 “bye” 以关闭您的 Windows 会话。

PowerShell 技能连载 - 查看所有模块的细节

powershellgallery.com 是找到新的免费 PowerShell 扩展模块的好地方,可以为您的 PowerShell 添加新的 cmdlet。

但是,在 Web 界面中查看所有模块详细信息可能会有点麻烦。这就是为什么通过 RESTful WebService 检索模块信息可能会有所帮助。

这是一个脚本,它传入 PowerShell Gallery 中托管的(任何)模块的名称。然后,它检索所有详细信息(例如版本历史记录、下载计数、更新日期和发行说明),并以一种使信息易于访问的方式准备它们。特别是,将检索到的基于 XML 的信息转换为简单对象:

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
44
45
46
47
48
# replace module name with any module name hosted
# in the PowerShell Gallery (https://powershellgallery.com)
$ModuleName = 'MicrosoftTeams'

$baseUrl = 'https://www.powershellgallery.com/api/v2/FindPackagesById()?id='
$escaped = [Uri]::EscapeDataString("'$ModuleName'")
$url = $baseUrl + $escaped

# properties to exclude (add or remove as needed)
$blacklist = 'FileList', 'Tags'

$data = Invoke-RestMethod -Uri $url -UseBasicParsing |
ForEach-Object {
$hash = [Ordered]@{}
$moduleInfo = $_.Properties
foreach($_ in $moduleInfo.PSObject.Properties)
{
# name of property
$name = $_.Name
# if it is in blacklist, skip and continue with next property
if ($name -in $blacklist) { continue }
# if it is the property "name", then skip
# all remaining (xml default properties)
if ($name -eq 'Name') { break }

# if type is "xmlelement", retrieve underlying text value in #text
if ($_.TypeNameOfValue -eq 'System.Xml.XmlElement')
{
$hash[$name] = $moduleInfo.$name.'#text'

# if a datatype is assigned, try and convert to appropriate type
if ($moduleInfo.$name.type -like 'Edm.*')
{
$typename = $moduleInfo.$name.type.replace('Edm.','')
$hash[$name] = $hash[$name] -as $typename
}
}
else
{
$hash[$name] = $_.Value
}
}

# convert a hash table to object and return it
[PSCustomObject]$hash
}

$data | Out-GridView
PowerShell 技术 QQ 群