PowerShell 技能连载 - 将哈希表转换为 JSON

在前一个技能中我们大量操作了哈希表,甚至从 .psd1 文件中读取。如果您需要不同格式的数据,例如 JSON,那么转换工作很简单。需要做的只是先将哈希表转换为一个对象:

1
2
3
4
5
6
7
8
9
$hash = @{
Name = 'Tobias'
ID = 12
Path = 'c:\windows'
}

$object = [PSCustomObject]$hash

$object | ConvertTo-Json

当哈希表转换为一个对象以后,您可以将它用管道传递给 ConvertTo-Json,或其它的 ConvertTo-* 指令。

PowerShell 技能连载 - 在 PowerShell 5+ 中读取 .PSD1 文件

在前一个技能中我们介绍了通过 Import-LocalizedData 读取存储在 .psd1 文件中的数据。

从 PowerShell 5 开始,有一个新的cmdlet名为 Import-PowerShellDataFile。您可以用它安全地从 .psd1 文件中读取数据。类似 Import-LocalizedData,这个cmdlet只接受没有活动内容(没有命令和变量)的 .psd1 文件。

以下是您需要的脚本:

1
2
$path = "$PSScriptRoot\data.psd1"
$infos = Import-PowerShellDataFile -Path $path

将数据文件存放在相同文件夹下,将它命名为 data.psd1,然后设为如下内容:

1
2
3
4
5
@{
Name = 'Tobias'
ID = 12
Path = 'c:\Windows'
}

当您运行这段脚本时,它将 .psd1 文件中的数据以哈希表的形式返回。

PowerShell 技能连载 - 从 .PSD1 文件中读取数据

一个脚本有多种方法可以保存数据信息。有一种方式特别方便。以下是实现的代码:

1
2
3
Import-LocalizedData -BaseDirectory $PSScriptRoot -FileName data.psd1 -BindingVariable Info

$Info

请确保将这段代码保存为一个脚本。然后在同一个文件夹中创建另一个文件,并命名为 “data.psd1”,然后增加这段内容:

1
2
3
4
5
@{
Name = 'Tobias'
ID = 12
Path = 'c:\Windows'
}

当两个文件都放在目录下时,运行脚本。它将读取 data.psd1 并将它的内容返回为一个哈希表。请注意 Import-LocalizedData 默认情况下并不能将 .psd1 文件作为活动的内容来处理。当 data.psd1 中的哈希表包含命令和变量时,它不可以读取——防止黑客纂改数据文件内容。

如果您在文件夹中添加了子文件夹,并且命名为语言区域性 ID,例如 “de-de” 和 “en-us”,Import-LocalizedData 将会自动检测合适的子目录并且从中读取文件(假设您将数据文件的本地化拷贝放在这些文件夹中)。该 cmdlet 将使用 $PSCulture 中提供的语言区域设置,或者如果指定了 -UICulture,将使用该设置。

PowerShell 技能连载 - 理解 PowerShell 中 .NET 类型名称的变体

PowerShell 使用 .NET 类型名,例如将值转换为指定的类型。脚本中常常可以使用各种格式来定义 .NET 类型。以下是它们各自的用意和含义:

1
2
3
4
5
6
7
8
# short name for "Integer" data type
[int]12.4
# official .NET type name
[system.int32]12.4
# here is how you get there
[int].FullName
# with official names, the namespace "System" is always optional
[int32]12.4

简单来说,PowerShell 维护着它自己的“类型加速器”:.NET 类型的别名。查看任意类型的 FullName 属性,可以获得完整正式的 .NET 类型名。类型名前面的 “System.” 是可以省略的。

PowerShell 技能连载 - 极简的错误处理

错误处理不必做的很复杂。它可以简单到检测上一条命令是否执行成功:

1
2
3
4
5
6
7
# suppress errors by default
$ErrorActionPreference = 'SilentlyContinue'
# if a command runs into an error...
Get-Process -Name zumsel
# ...then $? is $false, and you can exit PowerShell
# with a return value, i.e. 55
if (!$?) { exit 55 }

PowerShell 将上一个命令是否遇到错误的信息记录在 $? 变量中。在这个例子中,返回的是 $false。使用 exit 加上一个正数,就可以退出脚本,并且将退出码返回给调用者。

PowerShell 技能连载 - 临时禁用 PSReadLine 模块

从 PowerShell 5 开始,PowerShell 控制台支持彩色文本特性,以及一系列由 PSReadLine 模块提供的新特性。

如果您从更早的 PowerShell 版本升级到 PowerShell 5,而丢失了彩色文本功能,那么您可以从 PSGallery 下载和安装 PSReadLine 模块:

1
PS C:\> Install-Module -Name PSReadLine -Scope CurrentUser

类似地,如果您的 PS5+ 控制台和以前的行为不一致,例如不会执行粘贴入的代码块,那么您可以临时禁止该模块:

1
2
3
4
5
# disable PS5+ console handler temporarily
Remove-Module psreadline

# re-enable PS5+ console handler again
Import-Module psreadline

PowerShell 技能连载 -用 Windows 事件日志记录脚本日志

使用内置的 Windows 事件日志架构来记录脚本日志是很棒的方法,而且非常简单。以下是准备日志记录的初始步骤(需要管理员特权):

1
2
3
4
#requires -runasadministrator

New-EventLog -LogName PSScriptLog -Source Logon, Installation, Misc, Secret
Limit-EventLog -LogName PSScriptLog -MaximumSize 10MB -OverflowAction OverwriteAsNeeded

您可能需要改变日志的名称以及(或)错误源的名称。如果名字没有被占用,这些源可以起任意名称。

现在,每个普通用户可以使用以下代码来写入新的事件日志:

1
Write-EventLog -LogName PSScriptLog -Source Logon -EntryType Warning -EventId 123 -Message "Problem in script $PSCommandPath"

请注意 Write-EventLog 使用和前面定义相同的 logfile,并且 source 名称必须是前面定义过的之一。

但脚本写入信息到日志文件后,您可以搜索日志或者用 Get-EventLog 创建报告:

1
PS C:\> Get-EventLog -LogName PSScriptLog -EntryType Error -Message *test.ps1*

PowerShell 技能连载 - Getting File Extension

By converting a path to a FileInfo object, you can easily determine the path parent folder or file extension. Have a look:

([IO.FileInfo]'c:\test\abc.ps1').Extension

([IO.FileInfo]'c:\test\abc.ps1').DirectoryName

PowerShell 技能连载 - Working with [FileInfo] Object

Often, code needs to check on files, and for example test whether the file exists or exceeds a given size. Here is some commonly used code:

$logFile = "$PSScriptRoot\mylog.txt"

$exists = Test-Path -Path $logFile
if ($exists)
{
  $data = Get-Item -Path $logFile
  if ($data.Length -gt 100KB)
  {
    Remove-Item -Path $logFile
  }

}

By immediately converting a string path into a FileInfo object, you can do more with less:

[System.IO.FileInfo]$logFile = "$PSScriptRoot\mylog.txt"
if ($logFile.Exists -and $logFile.Length -gt 0KB) { Remove-Item -Path $logFile }

You can convert any path to a FileInfo object, even if it is not representing a file. That’s what the property “Exists” is for: it tells you whether the file is present or not.

Twitter This Tip!ReTweet this Tip!

PowerShell 技能连载 - 轻松记录脚本日志

从 PowerShell 5 开始,您可以在任何宿主中使用 Strart-Transcript 来记录脚本的所有输出内容。以下是向各种脚本轻松添加日志的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# add this: ############################
$logFile = "$PSScriptRoot\mylog.txt"
Start-Transcript -Path $logFile -Append
#########################################

"Hello"

($a = Get-Service)

"I received $($a.Count) services."
Write-Host "Watch out: direct output will not be logged!"


# end logging ###########################
Stop-Transcript
#########################################

只需要将注释块中的代码添加到脚本的开始和结束处。日志文件将会在脚本所在的目录创建。由于 $logFile 使用 $PSScriptRoot(脚本的当前文件夹),请确保已经将脚本保存并以脚本的方式运行。否则,$PSScriptRoot 变量可能为空。

只需要确保脚本输出所有您需要的信息,就可以在 logfile 中看到它。例如将赋值语句放在括号中,PowerShell 将不只是赋值,而且将它们输出到 output。

警告:除了用 Write-Host 直接写到宿主的信息,所有输入输出信息都将被记录下。这些信息只能在屏幕上看到。