PowerShell 技能连载 - 将 Windows 错误 ID 转换为友好的文字

当您在 PowerShell 中调用一个底层的函数时,您可能经常会得到一个数值型的返回值。如果这个返回值是来自一个 Windows API 函数,那么有一个非常简单的方法将它转换成有意义的文本:

例如,当一个 API 函数因为权限不足导致失败,它的返回值是 5。让我们将它翻译为一个有意义的文本:

1
2
PS> New-Object -TypeName ComponentModel.Win32Exception(5)
Access is denied

在 PowerShell 5 中,您还可以使用这种语法:

1
2
PS> [ComponentModel.Win32Exception]::new(5)
Access is denied

请试试这个例子:

1
1..200 | ForEach-Object { '{0} = {1}' -f $_, (New-Object -TypeName ComponentModel.Win32Exception($_)) }

PowerShell 技能连载 - 查找注册过的事件日志数据源名

当您用 Write-EventLog 将日志写入日志记录时,您需要指定一个合法的事件源名称。然而,并没有一个很方便的办法能查出哪个事件源文件对应注册到某个事件日志。这在您用 New-EventLog 创建新的事件日志时可能会带来麻烦:您不能指定一个已经存在的事件源名称。

以下是一个查找所有事件源名称,并且显示它们注册的事件日志的简单方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PS> Get-WmiObject -Class Win32_NTEventLOgFile | Select-Object FileName, Sources


FileName Sources
-------- -------
Application {Application, .NET Runtime, .NET Runtime Optimization Service, Application Error...}
Dell {Dell, DigitalDelivery, Update}
HardwareEvents {HardwareEvents}
Internet Explorer {Internet Explorer}
isaAgentLog {isaAgentLog, isaAgent}
Key Management Service {Key Management Service, KmsRequests}
OAlerts {OAlerts, Microsoft Office 16 Alerts}
PowerShellPrivateLog {PowerShellPrivateLog, Debug, Logon, Misc...}
PreEmptive {PreEmptive, PreEmptiveAnalytics}
Security {Security, DS, LSA, Microsoft-Windows-Eventlog...}
System {System, 3ware, ACPI, ADP80XX...}
TechSmith {TechSmith, TechSmith Uploader Service}
Windows PowerShell {Windows PowerShell, PowerShell}

您还可以将这个列表转换为一个有用的哈希表:

1
2
3
4
5
6
7
8
9
10
# find all registered sources
$Sources = Get-WmiObject -Class Win32_NTEventLOgFile |
Select-Object FileName, Sources |
ForEach-Object -Begin { $hash = @{}} -Process { $hash[$_.FileName] = $_.Sources } -end { $Hash }

# list sources for application log
$Sources["Application"]

# list sources for system log
$Sources["System"]

PowerShell 技能连载 - 使用事件日志方便地记录日志

脚本常常需要记录它们做了什么,并且 PowerShell 脚本开发者们需要做投入许多时间精力来将信息记录到文本文件中。

作为另一个选择,您可以方便地使用 Microsoft 已投入建设的工作:PowerShell 可以使用事件日志系统来记录信息。要做测试实验,请用以下代码创建一个新的事件日志。这部分需要管理员特权(写日志时不需要):

1
2
3
4
5
6
7
8
9
10
11
12
#requires -RunAsAdministrator

# name for your log
$LogName = 'PowerShellPrivateLog'
# size (must be dividable by 64KB)
$Size = 10MB

# specify a list of names that you'd use as source for your events
$SourceNames = 'Logon','Work','Misc','Test','Debug'

New-EventLog -LogName $LogName -Source $SourceNames
Limit-EventLog -LogName $LogName -MaximumSize $Size -OverflowAction OverwriteAsNeeded

当日志创建了以后,任何用户都可以记录日志文件:

1
2
3
4
5
6
7
8
9
10
11
12
PS> Write-EventLog -LogName PowerShellPrivateLog -Message 'Script Started' -Source Work -EntryType Information -EventId 1

PS> Write-EventLog -LogName PowerShellPrivateLog -Message 'Something went wrong!' -Source Work -EntryType Error -EventId 1



PS> Get-EventLog -LogName PowerShellPrivateLog | ft -AutoSize

Index Time EntryType Source InstanceID Message
----- ---- --------- ------ ---------- -------
2 Jan 30 21:57 Error Work 1 Something went wrong!
1 Jan 30 21:57 Information Work 1 Script Started

当您创建日志时,必须指定一个合法的 -Source 名称。使用这项技术的一个好处是您可以用 Get-EventLog 来方便地分析您的日志记录。

PowerShell 技能连载 - 还原短网址

类似 “http://bit.ly/e0Mw9w" 这样的网址十分短小,并且使用起来很方便。但它们往往屏蔽了原始的信息。

PowerShell 查找它们真实指向的地址,来还原短网址:

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

$request = [System.Net.WebRequest]::Create($url)
$request.AllowAutoRedirect=$false
$response=$request.GetResponse()
$trueUrl = $response.GetResponseHeader("Location")
"$url -> $trueUrl"

以下是使用结果:

http://bit.ly/e0Mw9w -> http://www.leeholmes.com/projects/ps_html5/Invoke-PSHtml5.ps1

PowerShell 技能连载 - 自动壁纸下载器

您对桌面壁纸感到厌倦了吗?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
function Download-Wallpaper
{
param
(
[string]
[Parameter(Mandatory)]
$Folder,

[Parameter(ValueFromPipeline)]
[Int]
$Page=1
)

begin
{
$url = "http://wallpaperswide.com/page/$Page"
$targetExists = Test-Path -Path $Folder
if (!$targetExists) { $null = New-Item -Path $Folder -ItemType Directory }
}
process
{
$web = Invoke-WebRequest -Uri $url -UseBasicParsing

$web.Images.src |
ForEach-Object {

$filename = $_.Split('/')[-1].Replace('t1.jpg','wallpaper-5120x3200.jpg')
$source = "http://wallpaperswide.com/download/$filename"

$TargetPath = Join-Path -Path $folder -ChildPath $filename

Invoke-WebRequest -Uri $source -OutFile $TargetPath
}
}
end
{
explorer $Folder
}
}

以下是使用方法:

1
PS> Download-Wallpaper -Folder c:\wallpapers

它将从一个公开的壁纸网站下载所有壁纸到本地文件夹,然后打开该文件夹。您所需要做的只是右键单击壁纸并且选择“设为桌面背景”。

默认情况下,Download-Wallpaper 从第一个页面下载壁纸。通过指定 -Page 参数,您可以从其它页面挖掘壁纸。请试试以下代码:

1
PS> Download-Wallpaper -Folder c:\wallpapers

PowerShell 技能连载 - 将值保存到 Excel 工作表中

有些时候,您可能会需要更新一个 Excel 工作表中的值。PowerShell 可以操作 Excel 对象模型,不过它很慢。以下是一个打开 Excel 文件,然后写入信息到 A1 单元格,最后保存更改的例子,

请确保您调整了路径,指向一个实际存在的 Excel 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$excel = New-Object -ComObject Excel.Application
# open Excel file
$workbook = $excel.Workbooks.Open("c:\test\excelfile.xlsx")

# uncomment next line to make Excel visible
#$excel.Visible = $true

$sheet = $workbook.ActiveSheet
$column = 1
$row = 1
# change content of Excel cell
$sheet.cells.Item($column,$row) = Get-Random
# save changes
$workbook.Save()
$excel.Quit()

PowerShell 技能连载 - 读取 Excel 单元格

有些时候,您可能会需要从 Excel 工作表中读取信息。PowerShell 可以操作 Microsoft Excel 对象模型,虽然它的速度很慢。

以下是一段延时如何操作 Excel 单元格的示例代码。请确保您调整了以下代码中的路径,指向一个实际存在的 Excel 文件。该代码将读取 A1 单元格的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$excel = New-Object -ComObject Excel.Application
# open Excel file
$workbook = $excel.Workbooks.Open("c:\test\excelfile.xlsx")

# uncomment next line to make Excel visible
#$excel.Visible = $true

$sheet = $workbook.ActiveSheet
$column = 1
$row = 1
$info = $sheet.cells.Item($column, $row).Text
$excel.Quit()


"Cell A1 contained '$info'"

PowerShell 技能连载 - 永久性设置环境变量

PowerShell 只能在它的进程空间里设置环境变量,所以这些改变无法保存,并且在 PowerShell 之外不可见。

要永久性地设置环境变量,可以编写一个简单的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Set-EnvironmentVariable
{
param
(
[string]
[Parameter(Mandatory)]
$Name,

[string]
[AllowEmptyString()]
[Parameter(Mandatory)]
$Value,

[System.EnvironmentVariableTarget]
[Parameter(Mandatory)]
$Target
)

[Environment]::SetEnvironmentVariable($Name, $Value, $Target)
}

现在您可以这样设置环境变量:

1
PS> Set-EnvironmentVariable -Name test -Value 123 -Target User

您也可以传入空字符串来移除一个环境变量。

1
PS> Set-EnvironmentVariable -Name test -Value "" -Target User

这是为什么 -Value 参数定义加上 [AllowEmptyString()] 属性的原因。如果没有这个属性,一个必选参数不能接受一个空字符串,那么该函数就无法移除环境变量。

另一个值得注意的地方是 -Target 参数的类型定义:因为制定了一个枚举类型,所以当您在 PowerShell ISE 或其它带有智能提示的编辑器中使用这个函数时,该编辑器将会贴心地提供智能提示选择。

PowerShell 技能连载 - Select-Object 和 -ExcludeProperty

以下是一行常常迷惑 PowerShell 用户的代码:

1
Get-Service | Select-Object -ExcludeProperty Name

当您使用 Select-Object 时,它的 -ExcludeProperty 参数并没有做任何事情。实际上,ExcludeProperty 只在使用 -Property 的时候才有效。所以这行代码是可以用的:

1
Get-Service | Select-Object -ExcludeProperty Name -Property Status, DisplayName, Name

然而,这看起来很荒谬:为什么通过 -Property 指定了属性,又还要用 ExcludeProperty 来排除它们呢?这样不是更简单吗:

1
Get-Service | Select-Object -Property Status, DisplayName

实际上,-ExcludeProperty 只在使用通配符的时候有意义:

1
2
3
4
5
6
7
8
PS> Get-CimInstance -ClassName Win32_BIOS | Select-Object -Property *BIOS* -ExcludeProperty *major*, *minor*


PrimaryBIOS : True
BiosCharacteristics : {7, 9, 11, 12...}
BIOSVersion : {DELL - 1072009, 1.6.1, American Megatrends - 5000B}
SMBIOSBIOSVersion : 1.6.1
SMBIOSPresent : True

PowerShell 技能连载 - 强制关闭所有 PowerShell ISE 文档

以下是一段强制关闭 PowerShell ISE 中所有打开的文档的代码片段。请注意:它不经提示就关闭所有的文档。它适用于当您搞砸了,并且不准备保存脚本的情况:

1
2
3
4
5
6
7
8
foreach ($tab in $psise.PowerShellTabs)
{
$files = $tab.Files
foreach ($file in $files)
{
$files.Remove($file, $true)
}
}

不过,当您运行这段代码时,您会收到一个错误。即便您不使用 PowerShell ISE,这个错误(和它的修复信息)对您来说可能十分有趣。

这段代码枚举出所有打开的文件并且尝试逐个关闭它们。这并不能工作:当您枚举一个数组时,您无法改变这个数组。所以当 PowerShell 关闭一个文档时,这个文件列表就变化了,而这将打断这个循环。

当发生这个错误时,一个简单的办法是先将这个数组拷贝到另一个数组。然后就可以安全地枚举这个副本数组。拷贝一个数组十分简单,只需要将它强制类型转换为 [Object[]]

以下是正确的代码:

1
2
3
4
5
6
7
8
foreach ($tab in $psise.PowerShellTabs)
{
$files = $tab.Files
foreach ($file in [Object[]]$files)
{
$files.Remove($file, $true)
}
}