PowerShell 技能连载 - 查找公网 IP 地址

这是一个单行程序,检索您当前的公共IP地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
PS> Invoke-RestMethod -Uri http://ipinfo.io


ip : 87.153.224.209
hostname : p5799e0d1.dip0.t-ipconnect.de
city : Hannover
region : Lower Saxony
country : DE
loc : 52.3705,9.7332
org : AS3320 Deutsche Telekom AG
postal : 30159
timezone : Europe/Berlin
readme : https://ipinfo.io/missingauth

PowerShell 技能连载 - 实时日志处理

PowerShell 提供了一种强大而简单的方法来监视文件更改。假设您有一个经常更改的日志文件。以下是一个PowerShell脚本,用于监视日志文件的更改。每当发生更改时,都会执行一些代码:

1
2
3
4
5
6
7
# make sure this points to a log file
$Path = '\\myserver\report2.txt'

Get-Content -Path $Path -Tail 0 -Wait |
ForEach-Object {
"Detected $_"
}

只要确保修改 $path 指向某个实际的日志文件。每当向文件附加文本(并且保存更改),ForEach-Object 循环都会执行脚本块并输出 “Detected “。通过这种方式,您可以方便地响应实际的改变。

Get-Content 完成繁重的工作:-Wait 启用内容监视,-Tail 0 确保忽略现有内容,只查找新添加的文本。

PowerShell 技能连载 - 检测键盘按键

通常,只有在真正的控制台窗口中才支持按键检测,因此这种方法不适用于 PowerShell ISE 和其他 PowerShell 宿主。

但是,PowerShell 可以从 Windows Presentation Foundation 中借用一种类型,这种类型可以检查任何键的状态。这样,实现在任何 PowerShell 脚本中都可以工作的“退出”键就变得很简单了,无论是在控制台、Visual Studio Code 还是 PowerShell ISE 中运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Add-Type -AssemblyName WindowsBase
Add-Type -AssemblyName PresentationCore

# choose the abort key
$key = [System.Windows.Input.Key]::LeftCtrl

Write-Warning "PRESS $key TO ABORT!"

do
{
$isCtrl = [System.Windows.Input.Keyboard]::IsKeyDown($key)
if ($isCtrl)
{
Write-Host
Write-Host "You pressed $key, so I am exiting!" -ForegroundColor Green
break
}
Write-Host "." -NoNewline
Start-Sleep -Milliseconds 100
} while ($true)

只需要在变量 $key 中选择“退出”按键即可。本例使用的是左 CTRL 键。

PowerShell 技能连载 - 检测存储问题

在 Windows 10 和 Windows Server 2016 中,PowerShell 可以访问存储可靠性数据,这样您就可以发现其中一个附加的存储驱动器是否有问题。这需要管理员特权来执行:

1
2
3
4
5
6
PS> Get-PhysicalDisk | Get-StorageReliabilityCounter

DeviceId Temperature ReadErrorsUncorrected Wear PowerOnHours
-------- ----------- --------------------- ---- ------------
0 0
1 0

要查看所有可用信息,请使用 Select-Object

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
PS> Get-PhysicalDisk | Get-StorageReliabilityCounter | Select-Object -Property *


(...)
DeviceId : 0
FlushLatencyMax : 104
LoadUnloadCycleCount :
LoadUnloadCycleCountMax :
ManufactureDate :
PowerOnHours :
ReadErrorsCorrected :
ReadErrorsTotal :
ReadErrorsUncorrected :
ReadLatencyMax : 1078
StartStopCycleCount :
StartStopCycleCountMax :
Temperature : 1
TemperatureMax : 1
Wear : 0
WriteErrorsCorrected :
WriteErrorsTotal :
WriteErrorsUncorrected :
WriteLatencyMax : 1128
(...)
FlushLatencyMax :
LoadUnloadCycleCount :
LoadUnloadCycleCountMax :
ManufactureDate :
PowerOnHours :
ReadErrorsCorrected :
ReadErrorsTotal :
ReadErrorsUncorrected :
ReadLatencyMax : 46
StartStopCycleCount :
StartStopCycleCountMax :
Temperature : 0
TemperatureMax : 0
Wear : 0
WriteErrorsCorrected :
WriteErrorsTotal :
WriteErrorsUncorrected :
WriteLatencyMax :
PSComputerName :
(...)

详情和返回数据的数量取决于您的存储制造商和您的驱动器。

PowerShell 技能连载 - 重设 Winsock

PowerShell 既可以运行内置的 PowerShell 命令,也可以运行常规的控制台命令,所以继续用控制台命令处理已知的任务也不是件坏事。

例如,如果您希望重设 winsock,以下是一个可信赖的解决方案:

1
2
3
4
#requires -RunAsAdministrator

netsh winsock reset
netsh int ip reset

请注意这段代码需要管理员特权,并且可能需要重启才能生效。

PowerShell 技能连载 - 使用超棒的 Export-Excel Cmdlet(第 5 部分)

这是我们关于 Doug Finke 的强大而免费的 “ImportExcel” PowerShell 模块的迷你系列文章的第 5 部分。在学习这个技能之前,请确保安装了该模块:

1
PS> Install-Module -Name ImportExcel -Scope CurrentUser -Force

在第 4 部分中,我们研究了由于在输入数据中包含数组而导致的误读数据。正如您所看到的,您只需要使用 -join 操作符将数组转换为字符串,Excel 就可以正确地显示数组,即逗号分隔值的列表。

但是,如果希望在单独的行中显示数组元素,并使用换行符呢?

默认情况下,Excel 只会在选定单元格的输入框中显示单独的行,而不是在所有单元格中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# get some raw data that contains arrays
$rawData = Get-EventLog -LogName System -Newest 10 |
Select-Object -Property TimeWritten, ReplacementStrings, InstanceId


# create this Excel file
$Path = "$env:temp\report.xlsx"
# make sure the file is deleted so we have no
# effects from previous data still present in the
# file. This requires that the file is not still
# open and locked in Excel
$exists = Test-Path -Path $Path
if ($exists) { Remove-Item -Path $Path}

$sheetName = 'Testdata'
$rawData |
ForEach-Object {
# convert column "ReplacementStrings" from array to string
$_.ReplacementStrings = $_.ReplacementStrings -join "`r`n"
# return the changed object
$_
} |
Export-Excel -Path $path -ClearSheet -WorksheetName $sheetName -Show

当您运行这段代码时,”ReplacementStrings” 中的数组将会正确地转换为多行文本,但是您不会在工作表中看到它。只有当您单击某个单元格时才会看到输入区域中显示多行文本。

当您把我们前面部分的信息组合起来时,可以很容易地对 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
# get some raw data that contains arrays
$rawData = Get-EventLog -LogName System -Newest 10 |
Select-Object -Property TimeWritten, ReplacementStrings, InstanceId


# create this Excel file
$Path = "$env:temp\report.xlsx"
# make sure the file is deleted so we have no
# effects from previous data still present in the
# file. This requires that the file is not still
# open and locked in Excel
$exists = Test-Path -Path $Path
if ($exists) { Remove-Item -Path $Path}

$sheetName = 'Testdata'

# save the Excel object model by using -PassThru instead of -Show
$excel = $rawData |
ForEach-Object {
# convert column "ReplacementStrings" from array to string
$_.ReplacementStrings = $_.ReplacementStrings -join "`r`n"
# return the changed object
$_
} |
Export-Excel -Path $path -ClearSheet -WorksheetName $sheetName -AutoSize -PassThru

#region Post-process the column with the misinterpreted formulas
# remove the region to repro the original Excel error
$sheet1 = $excel.Workbook.Worksheets[$sheetName]
# reformat cell to number type "TEXT" with WordWrap and AutoSize
Set-Format -Address $sheet1.Cells['B:B'] -NumberFormat 'Text' -WrapText -AutoSize
#endregion

Close-ExcelPackage -ExcelPackage $excel -Show

PowerShell 技能连载 - 使用超棒的 Export-Excel Cmdlet(第 4 部分)

这是我们关于 Doug Finke 的强大而免费的 “ImportExcel” PowerShell 模块的迷你系列文章的第 4 部分。在学习这个技能之前,请确保安装了该模块:

1
PS> Install-Module -Name ImportExcel -Scope CurrentUser -Force

在第 3 部分中,我们研究了由于公式自动转换而导致的错误解析数据,并研究了后期处理单个单元格格式的方式。让我们检查一下数组引起的问题。

以下是一些重现该现象的代码。在我们的示例中,这是最后 10 条系统事件的事件日志数据,它恰好包含了一个数组(替换字符串),并且显示完全不正常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# get some raw data that contains arrays
$rawData = Get-EventLog -LogName System -Newest 10 |
Select-Object -Property TimeWritten, ReplacementStrings, InstanceId


# create this Excel file
$Path = "$env:temp\report.xlsx"
# make sure the file is deleted so we have no
# effects from previous data still present in the
# file. This requires that the file is not still
# open and locked in Excel
$exists = Test-Path -Path $Path
if ($exists) { Remove-Item -Path $Path}

$sheetName = 'Testdata'
$rawData |
Export-Excel -Path $path -ClearSheet -WorksheetName $sheetName -Show

当 Excel 打开时,您可以看见 “ReplacementStrings” 列只显示数据类型 (System.String[]) 而不是实际的数据。这是 Excel 遇到数组的通常行为,所以 Export-Excel 对此无能为力。

相反地,在将将数组通过管道输出到 Export-Excel 命令之前转换为字符串是您的责任——用 -join 操作符可以很容易实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# get some raw data that contains arrays
$rawData = Get-EventLog -LogName System -Newest 10 |
Select-Object -Property TimeWritten, ReplacementStrings, InstanceId


# create this Excel file
$Path = "$env:temp\report.xlsx"
# make sure the file is deleted so we have no
# effects from previous data still present in the
# file. This requires that the file is not still
# open and locked in Excel:
$exists = Test-Path -Path $Path
if ($exists) { Remove-Item -Path $Path}

$sheetName = 'Testdata'
$rawData |
ForEach-Object {
# convert column "ReplacementStrings" from array to string
$_.ReplacementStrings = $_.ReplacementStrings -join ','
# return the changed object
$_
} |
Export-Excel -Path $path -ClearSheet -WorksheetName $sheetName -Show

当您做了这步操作之后,包含数组的属性在 Excel 中也可以正确显示。-join 对任何对象都有效。只需要确保指定了分割数组元素的分隔符。

PowerShell 技能连载 - 使用超棒的 Export-Excel Cmdlet(第 3 部分)

这是我们关于 Doug Finke 的强大而免费的 “ImportExcel” PowerShell 模块的迷你系列文章的第 3 部分。在学习这个技能之前,请确保安装了该模块:

1
PS> Install-Module -Name ImportExcel -Scope CurrentUser -Force

在第 2 部分中,我们检查了由于数字自动转换导致的错误解释。当原始数据”看起来像“ Excel 公式时会导致另一个问题,它们会被转换为公式并且会在等等打开时出现问题。

以下是重现该问题的示例:一些记录包含以 “=)” 开头的文本,会导致 Excel 认为它是一个公式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# any object-oriented data will do
# we create some sample records via CSV
# to mimick specific issues
$rawData = @'
Data,Name
Test, Tobias
=), Mary
=:-(), Tom
'@ | ConvertFrom-Csv

# create this Excel file
$Path = "$env:temp\report.xlsx"
# make sure the file is deleted so we have no
# effects from previous data still present in the
# file. This requires that the file is not still
# open and locked in Excel
$exists = Test-Path -Path $Path
if ($exists) { Remove-Item -Path $Path}

$rawData |
Export-Excel -Path $path -ClearSheet -WorksheetName Processes -Show

当您运行这段代码时,Excel 将打开但是立即报告非法格式。原始数据将会丢失。

这个问题无法通过一个开关参数解决。相反,您需要手动重新格式化单元格,这给了您很大的灵活性。以下是总体的策略:

  • 使用 Export-Excel 创建 .xlsx 文件,但不是指定 -Show(在 Excel 中打开文件),而是使用 -PassThru。这样就得到了 Excel 对象模型。
  • 使用对象模型对单元格进行任意更改
  • 使用 Close-ExcelPackage 将更改保存到文件中。您现在可以指定 -Show,并在 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
# any object-oriented data will do
# we create some sample records via CSV
# to mimick specific issues
$rawData = @'
Data,Name
Test, Tobias
=), Mary
=:-(), Tom
'@ | ConvertFrom-Csv

# create this Excel file
$Path = "$env:temp\report.xlsx"
# make sure the file is deleted so we have no
# effects from previous data still present in the
# file. This requires that the file is not still
# open and locked in Excel
$exists = Test-Path -Path $Path
if ($exists) { Remove-Item -Path $Path}

$sheetName = 'Testdata'
$excel = $rawData |
Export-Excel -Path $path -ClearSheet -WorksheetName $sheetName -PassThru



#region Post-process the column with the misinterpreted formulas
# remove the region to repro the original Excel error
$sheet1 = $excel.Workbook.Worksheets[$sheetName]

# take all cells from row "A"...
$sheet1.Cells['A:A'] |
# ...that are currently interpreted as a formula...
Where-Object Formula |
ForEach-Object {
# ...construct the original content which is the formula
# plus a prepended "="
$newtext = ('={0}' -f $_.Formula)
# reformat cell to number type "TEXT"
Set-Format -Address $_ -NumberFormat 'Text' -Value 'dummy'
# assign the original content to the cell (this cannot be done using Set-Format)
$_.Value = $newtext
}
#endregion

Close-ExcelPackage -ExcelPackage $excel -Show

当您运行这段代码时,Excel 工作簿打开时不会报错,并且第一列能够正确地显示内容。这是由于我们显式地将第一列格式化为“文本”。然后,一旦格式被设置为“文本”,那么公式内容就会作为单元格值插入。

您不会受到“公式”错误信息,也不必通过在其周围添加引号来“屏蔽”内容。

这个示例演示了如何后期处理 Excel 工作簿并且在将结果保存到文件并在 Excel 中打开结果之前增加、更改、重新格式化独立的单元格。

PowerShell 技能连载 - 使用超棒的 Export-Excel Cmdlet(第 2 部分)

这是我们关于 Doug Finke 的强大而免费的 “ImportExcel” PowerShell 模块的迷你系列文章的第 2 部分。在学习这个技能之前,请确保安装了该模块:

1
PS> Install-Module -Name ImportExcel -Scope CurrentUser -Force

当您导出数据到 Excel 文件中时,您有时可能会遇到 Excel 错误解释的数据。例如,电话号码常常被错误地解释为数字型数据。以下是一个重现该问题的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# any object-oriented data will do
# we create some sample records via CSV
# to mimick specific issues
$rawData = @'
Phone,Name
+4915125262524, Tobias
0766256725672, Mary
00496253168722567, Tom
'@ | ConvertFrom-Csv

# create this Excel file
$Path = "$env:temp\report.xlsx"
# make sure the file is deleted so we have no
# effects from previous data still present in the
# file. This requires that the file is not still
# open and locked in Excel
$exists = Test-Path -Path $Path
if ($exists) { Remove-Item -Path $Path}

$rawData |
Export-Excel -Path $path -ClearSheet -WorksheetName Processes -Show

如您所见,当 Excel 打开时,电话号码自动转换为整形。

要避免这个自动转换,请使用 -NoNumberConversion 参数,并且指定不需要转换的列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# any object-oriented data will do
# we create some sample records via CSV
# to mimick specific issues
$rawData = @'
Phone,Name
+4915125262524, Tobias
0766256725672, Mary
00496253168722567, Tom
'@ | ConvertFrom-Csv

# create this Excel file
$Path = "$env:temp\report.xlsx"
# make sure the file is deleted so we have no
# effects from previous data still present in the
# file. This requires that the file is not still
# open and locked in Excel
$exists = Test-Path -Path $Path
if ($exists) { Remove-Item -Path $Path}

$rawData |
Export-Excel -Path $path -ClearSheet -WorksheetName Processes -Show -NoNumberConversion Phone

现在,”Phone” 列不再处理为数字,电话号码显示正常了。

PowerShell 技能连载 - 使用超棒的 Export-Excel Cmdlet(第 1 部分)

Doug Finke 创建了一个非常棒的 PowerShell 模块 ImportExcel,它提供了从 Microsoft Excel 导入和导出数据所需的所有命令。它不需要安装Office。

我们不能涵盖这个模块提供的所有内容,但在本文中,我们将为您提供启动和运行它的基础知识,在后续的技巧中,我们将讨论一些格式化技巧。

要使用 Excel 命令,只需要下载并安装免费的模块:

1
PS> Install-Module -Name ImportExcel -Scope CurrentUser -Force

首次运行时,您可能必须同意下载一个 “NuGet” 开源DLL。命令完成后,您现在可以访问大量新的 Excel 命令,其中最重要的是 Export-Excel

您现在可以通过管道将数据直接传给一个 Excel 文件,并且假设在 Microsoft Office 已经安装的情况下,您甚至可以在 Excel 中打开并显示文件(创建 .xlsx 文件不需要 Office)。

以下是一个简单的示例:

1
2
3
$Path = "$env:temp\report.xlsx"
Get-Process | Where-Object MainWindowTitle |
Export-Excel -Path $path -ClearSheet -WorksheetName Processes -Show

就是这么简单。创建 Excel 文件从来没有这么容易过。不过,你要记住以下几点:

  • 在将数据通过管道传给 Export-Excel 之前,使用 Select-Object 选择要导出的属性。
  • 使用 -ClearSheet 清除以前的数据。如果省略此参数,新数据将附加到 .xlsx 文件中的现有数据之后。
  • 在创建具有相同名称的新文件之前,您可能需要考虑手动删除旧的 .xlsx 文件。否则,Export-Excel 可能会参考旧文件中的现有设置。