PowerShell 技能连载 - 查看 Windows 通用唯一识别码 (UUID)

每个 Windows 的安装都有一个唯一的 UUID,您可以用它来区分机器。计算机名可能会改变,但 UUID 不会。

1
2
PS> (Get-CimInstance -Class Win32_ComputerSystemProduct).UUID
4C4C4544-004C-4710-8051-C4C04F443732

In reality, the UUID is just a GUID (Globally Unique Identifier), which comes in different formats:
实际中,UUID 只是一个 GUID(全局唯一标识符),它的格式有所不同:

1
2
3
4
5
6
7
$uuid = (Get-CimInstance -Class Win32_ComputerSystemProduct).UUID
[Guid]$guid = $uuid

"d","n","p","b","x" |
ForEach-Object {
'$guid.ToString("{0}") = {1}' -f $_, $guid.ToString($_)
}

以下是执行结果:

$guid.ToString("d")= 4c4c4544-004c-4710-8051-c4c04f443732
$guid.ToString("n")= 4c4c4544004c47108051c4c04f443732
$guid.ToString("p")= (4c4c4544-004c-4710-8051-c4c04f443732)
$guid.ToString("b")= {4c4c4544-004c-4710-8051-c4c04f443732}
$guid.ToString("x")= {0x4c4c4544,0x004c,0x4710,{0x80,0x51,0xc4,0xc0,0x4f,0x44,0x37,0x32}}

如果您希望为某个想标记的东西创建一个新的 UUID(或 GUID),例如临时文件名,那么可以用 PowerShell 5 新带来的 New-Guid 命令:

1
2
3
4
5
PS> New-Guid

Guid
----
16750457-9a7e-4510-96ab-f9eef7273f3e

它实质上是在后台调用了这个 .NET 方法:

1
2
3
4
5
PS> [Guid]::NewGuid()

Guid
----
6cb3cb1a-b094-425b-8ccb-e74c2034884f

PowerShell 技能连载 - 格式化日期和时间(包含区域性)

在前一个技能中我们演示了 Get-Date 如何用格式化字符串将 DateTime 值转换为字符串。不过该字符串转换总是使用操作系统中的语言。这可能不是您想要的。现在我们来解决这个问题:

以下是输出 2018 圣诞前夕是周几的示例:

1
2
3
$christmasEve = Get-Date -Date '2018-12-24'

Get-Date -Date $christmasEve -Format '"Christmas Eve in" yyyy "will be on" dddd.'

显然这是在德文系统上做的转换,所以结果中的周几是用德文显示的:

Christmas Eve in 2018 will be on Montag.

如果您的脚本需要输出不同语言的结果,例如以英语(或其他语言)的方式来输出星期几。要控制语言,您需要意识到两件事:第一,Get-Date-Format 的格式化选项只是通用的 .NET 方法 ToString() 的简单封装,所以您也可以运行这段代码获得相同的结果:

1
2
3
$christmasEve = Get-Date -Date '2018-12-24'

$christmasEve.ToString('"Christmas Eve in" yyyy "will be on" dddd.')

第二,ToString() 方法有许多重载,其中一个能接受任何实现了 IFormatProvider 接口的对象,它们恰好包含了 “CultureInfo“ 对象:

1
2
3
4
5
6
7
8
9
10
PS> $christmasEve.ToString

OverloadDefinitions
-------------------
string ToString()
string ToString(string format)
string ToString(System.IFormatProvider provider)
string ToString(string format, System.IFormatProvider provider)
string IFormattable.ToString(string format, System.IFormatProvider formatProvider)
string IConvertible.ToString(System.IFormatProvider provider)

以下是无论在什么语言的操作系统上都以英文输出周几的解决方案:

1
2
3
4
5
6
$christmasEve = Get-Date -Date '2018-12-24'
$culture = [CultureInfo]'en-us'
$christmasEve.ToString('"Christmas Eve in" yyyy "will be on" dddd.', $culture)


Christmas Eve in 2018 will be on Monday.

如果要显示其它地区的语言,例如要查看中文或泰文中“星期一”的表达:

1
2
3
4
5
6
7
8
9
10
$christmasEve = Get-Date -Date '2018-12-24'
$culture = [CultureInfo]'zh'
$christmasEve.ToString('"Monday in Chinese: " dddd.', $culture)
$culture = [CultureInfo]'th'
$christmasEve.ToString('"Monday in Thai: " dddd.', $culture)



Monday in Chinese: 星期一.
Monday in Thai: จันทร์.

PowerShell 技能连载 - 格式化日期和时间

通过 Get-Date-Format 参数可以方便地将日期和时间格式化为您所需的格式。您可以对当前时间使用它,也可以对外部的 DateTime 变量使用它。只需要使用日期和时间的格式化字符串就可以转换为您所需的输出格式。

以下是一些例子。例如要将当前日期暗 ISO 格式输出,请运行以下代码:

1
2
PS> Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
2018-12-02 11:36:37

使用现有的或从别处读取的 datetime 对象的方法是 将它传给 Get-Date-Date 属性:

1
2
3
4
5
6
7
8
9
# find out last boot time
$os = Get-CimInstance -ClassName Win32_OperatingSystem
$lastBoot = $os.LastBootUpTime

# raw datetime output
$lastBoot

# formatted string output
Get-Date -Date $lastBoot -Format '"Last reboot at" MMM dd, yyyy "at" HH:mm:ss "and" fffff "Milliseconds.

格式化字符串既可以包括日期时间的通配符,也可以包括静态文本。只需要确保用双引号将静态文本包括起来。以下是执行结果(在德语系统上):

Donnerstag, 22. November 2018 01:13:44

Last reboot at Nov 22, 2018 at 01:13:44 and 50000 Milliseconds.

PowerShell 技能连载 - 将 PowerShell 结果发送到 PDF(第 4 部分)

在前一个技能中我们将建了 Out-PDFFile 函数,能够接受任意 PowerShell 的结果数据并将它们转换为 PDF 文件——使用 Windows 10 和 Windows Server 2016 内置的打印驱动。

我们使用了一个简单的函数,用 $Input 自动变量来读取管道输入的数据,从而达到上述目的。如果您更希望使用高级函数,利用它们的必选参数等功能,我们将该工程包装成一个更优雅的高级函数,它能够在检测到未安装 PDF 打印机的情况下先安装它:

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
function Out-PDFFile
{
param
(
[Parameter(Mandatory)]
[String]
$Path,

[Parameter(ValueFromPipeline)]
[Object]
$InputObject,

[Switch]
$Open
)

begin
{
# check to see whether the PDF printer was set up correctly
$printerName = "PrintPDFUnattended"
$printer = Get-Printer -Name $printerName -ErrorAction SilentlyContinue
if (!$?)
{
$TempPDF = "$env:temp\tempPDFResult.pdf"
$port = Get-PrinterPort -Name $TempPDF -ErrorAction SilentlyContinue
if ($port -eq $null)
{
# create printer port
Add-PrinterPort -Name $TempPDF
}

# add printer
Add-Printer -DriverName "Microsoft Print to PDF" -Name $printerName -PortName $TempPDF
}
else
{
# this is the file the print driver always prints to
$TempPDF = $printer.PortName

# is the port name is the output file path?
if ($TempPDF -notlike '?:\*')
{
throw "Printer $printerName is not set up correctly. Remove the printer, and try again."
}
}

# make sure old print results are removed
$exists = Test-Path -Path $TempPDF
if ($exists) { Remove-Item -Path $TempPDF -Force }

# create an empty arraylist that takes the piped results
[Collections.ArrayList]$collector = @()
}

process
{
$null = $collector.Add($InputObject)
}

end
{
# send anything that is piped to this function to PDF
$collector | Out-Printer -Name $printerName

# wait for the print job to be completed, then move file
$ok = $false
do {
Start-Sleep -Milliseconds 500
Write-Host '.' -NoNewline

$fileExists = Test-Path -Path $TempPDF
if ($fileExists)
{
try
{
Move-Item -Path $TempPDF -Destination $Path -Force -ea Stop
$ok = $true
}
catch
{
# file is still in use, cannot move
# try again
}
}
} until ( $ok )
Write-Host

# open file if requested
if ($Open)
{
Invoke-Item -Path $Path
}
}
}

假设您使用的是 Windows 10 或 Windows 2016,并且 “Microsoft Print to PDF” 打印机可用,那么您可以像这样方便地创建 PDF 文档:

1
2
3
PS> Get-Service | Out-PDFFile -Path $home\desktop\services.pdf -Open

PS> Get-ComputerInfo | Out-PDFFile -Path $home\Desktop\computerinfo.pdf -Open

如果指定的 “PrintPDFUnattended” 打印机还未安装,该函数也会事先安装该打印机。

PowerShell 技能连载 - 将 PowerShell 结果发送到 PDF(第 3 部分)

在前一个技能中我们解释了如何在 Windows 10 和 Windows Server 2016 中使用 PowerShell 来设置一个能将任何东西打印到 PDF 文件的打印机,当然,是无人值守的。要使它真的发挥作用,我们将它封装为一个名为 Out-PDFFile 的函数。任何通过管道传给这个新命令的内容都会被转换为一个 PDF 文件。

注意:要让这个函数生效,您必须按前一个技能介绍的方法先创建一个名为 PrintPDFUnattended 的打印机!

以下是 Out-PDFFile 函数:

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
function Out-PDFFile
{
param
(
$Path = "$env:temp\results.pdf",

[Switch]
$Open
)

# check to see whether the PDF printer was set up correctly
$printerName = "PrintPDFUnattended"
$printer = Get-Printer -Name $printerName -ErrorAction SilentlyContinue
if (!$?)
{
Write-Warning "Printer $printerName does not exist."
Write-Warning "Make sure you have created this printer (see previous tips)!"
return
}

# this is the file the print driver always prints to
$TempPDF = $printer.PortName

# is the printer set up correctly and the port name is the output file path?
if ($TempPDF -notlike '?:\*')
{
Write-Warning "Printer $printerName is not set up correctly."
Write-Warning "Make sure you have created this printer as instructed (see previous tips)!"
return
}

# make sure old print results are removed
$exists = Test-Path -Path $TempPDF
if ($exists) { Remove-Item -Path $TempPDF -Force }

# send anything that is piped to this function to PDF
$input | Out-Printer -Name $printerName

# wait for the print job to be completed, then move file
$ok = $false
do {
Start-Sleep -Milliseconds 500
Write-Host '.' -NoNewline

$fileExists = Test-Path -Path $TempPDF
if ($fileExists)
{
try
{
Move-Item -Path $TempPDF -Destination $Path -Force -ea Stop
$ok = $true
}
catch
{
# file is still in use, cannot move
# try again
}
}
} until ( $ok )
Write-Host

# open file if requested
if ($Open)
{
Invoke-Item -Path $Path
}
}

现在导出结果到 PDF 文件十分简单:

1
2
3
PS> Get-Service | Out-PDFFile -Path $home\desktop\services.pdf -Open

PS> Get-ComputerInfo | Out-PDFFile -Path $home\Desktop\computerinfo.pdf -Open

哇哦,真简单!

请注意我们有意地创建了一个“简单函数”。通过这种方式,所有通过管道输入的数据都可以在 $Input 自动变量中见到。如果您向参数添加属性,例如要使参数成为必选的,这个函数就变成了“高级函数”,并且 $Input 就不存在了。我们将在明天解决这个问题。


psconf.eu – PowerShell Conference EU 2019 – June 4-7, Hannover Germany – visit www.psconf.eu There aren’t too many trainings around for experienced PowerShell scripters where you really still learn something new. But there’s one place you don’t want to miss: PowerShell Conference EU - with 40 renown international speakers including PowerShell team members and MVPs, plus 350 professional and creative PowerShell scripters. Registration is open at www.psconf.eu, and the full 3-track 4-days agenda becomes available soon. Once a year it’s just a smart move to come together, update know-how, learn about security and mitigations, and bring home fresh ideas and authoritative guidance. We’d sure love to see and hear from you!

PowerShell 技能连载 - 将 PowerShell 结果发送到 PDF(第 2 部分)

在前一个示例中我们延时了如何使用内置的 “Microsoft Print to PDF” 打印机来将 PowerShell 输出结果发送到 PDF 文件。然而,这个打印机会提示选择选择输出的文件,所以不适合自动化任务。

要禁止文件提示,有一个很少人知道的秘密:只需要对打印机指定一个端口,端口名就是输出的文件路径。换句话说,运行这段代码可以创建一个新打印机,并打印到您选择的文件中:

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
# requires Windows 10 / Windows Server 2016 or better

# choose a name for your new printer
$printerName = 'PrintPDFUnattended'
# choose a default path where the PDF is saved
$PDFFilePath = "$env:temp\PDFResultFile.pdf"
# choose whether you want to print a test page
$TestPage = $true

# see whether the driver exists
$ok = @(Get-PrinterDriver -Name "Microsoft Print to PDF" -ea 0).Count -gt 0
if (!$ok)
{
Write-Warning "Printer driver 'Microsoft Print to PDF' not available."
Write-Warning "This driver ships with Windows 10 or Windows Server 2016."
Write-Warning "If it is still not available, enable the 'Printing-PrintToPDFServices-Features'"
Write-Warning "Example: Enable-WindowsOptionalFeature -Online -FeatureName Printing-PrintToPDFServices-Features"
return
}

# check whether port exists
$port = Get-PrinterPort -Name $PDFFilePath -ErrorAction SilentlyContinue
if ($port -eq $null)
{
# create printer port
Add-PrinterPort -Name $PDFFilePath
}

# add printer
Add-Printer -DriverName "Microsoft Print to PDF" -Name $printerName -PortName $PDFFilePath

# print a test page to the printer
if ($TestPage)
{
$printerObject = Get-CimInstance Win32_Printer -Filter "name LIKE '$printerName'"
$null = $printerObject | Invoke-CimMethod -MethodName printtestpage
Start-Sleep -Seconds 1
Invoke-Item -Path $PDFFilePath
}

当这段脚本执行以后,可以获得一个全新的名为 PrintPDFUnattended 的打印机。并且当打印到该打印机不会产生提示,而是永远输出到临时文件夹的 PDFResultFile.pdf

以下是一段演示如何从 PowerShell 打印 PDF 文件而不产生对话框的方法:

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
# specify the path to the file you want to create
# (adjust if you want)
$OutPath = "$home\desktop\result.pdf"

# this is the file the print driver always prints to
$TempPDF = "$env:temp\PDFResultFile.pdf"

# make sure old print results are removed
$exists = Test-Path -Path $TempPDF
if ($exists) { Remove-Item -Path $TempPDF -Force }

# send PowerShell results to PDF
Get-Service | Out-Printer -Name "PrintPDFUnattended"

# wait for the print job to be completed, then move file
$ok = $false
do {
Start-Sleep -Milliseconds 500
Write-Host '.' -NoNewline

$fileExists = Test-Path -Path $TempPDF
if ($fileExists)
{
try
{
Move-Item -Path $TempPDF -Destination $OutPath -Force -ErrorAction Stop
$ok = $true
}
catch
{
# file is still in use, cannot move
# try again
}
}
} until ( $ok )
Write-Host

# show new PDF file in explorer
explorer "/select,$OutPath"

当运行这段代码时,将会在桌面上创建一个新的 result.pdf 文件。它包含了所有服务的列表。您可以将任何结果通过管道输出到 Out-Printer 来创建 PDF 文件。

PowerShell 技能连载 - 将 PowerShell 结果发送到 PDF(第 1 部分)

Windows 10 和 Windows Server 2016 终于带来了内置的 PDF 打印机,名为 “Microsoft Print to PDF”。您可以在 PowerShell 中用它来创建 PDF 文件。请运行这段代码来测试您的 PDF 打印机:

1
2
3
4
5
6
7
8
9
$printer = Get-Printer -Name "Microsoft Print to PDF" -ErrorAction SilentlyContinue
if (!$?)
{
Write-Warning "Your PDF Printer is not yet available!"
}
else
{
Write-Warning "PDF printer is ready for use."
}

如果您的打印机不能用(或是暂时不能用),那么您可能使用的不是 Windows 10 或 Windows Server 2016,或者 PDF 打印功能尚未启用。请在管理员权限下运行 PowerShell 并执行这段命令来修复它:

1
PS> Enable-WindowsOptionalFeature -Online -FeatureName Printing-PrintToPDFServices-Features

请确保使用管理员权限运行上述代码,您也可以试着使用以下代码:

1
2
3
$code = 'Enable-WindowsOptionalFeature -Online -FeatureName Printing-PrintToPDFServices-Features'

Start-Process -Verb Runas -FilePath powershell.exe -ArgumentList "-noprofile -command $code"

当 PDF 打印机安装好后,从 PowerShell 中创建 PDF 文件十分简单。只需要将输出结果发送到 Out-Printer。以下是一个示例:

1
PS> Get-Service | Out-Printer -Name "Microsoft Print to PDF"

打印机驱动将会打开一个对话框,您可以选择输出的文件名。如果不希望显示这个对话框,而以无人值守的方式打印,我们将在明天介绍。

PowerShell 技能连载 - 在打印机上打印测试页

感谢 PrintManagement 模块为 Windows 10 和 Windows Server 2016 提供了大量的打印功能支持。如果希望打印官方的测试页,您还需要动用 WMI。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#requires -Version 3.0 -Modules CimCmdlets, PrintManagement

Get-Printer |
Out-GridView -Title 'Print test page on selected printers' -OutputMode Multiple |
ForEach-Object {
$printerName = $_.Name
$result = Get-CimInstance Win32_Printer -Filter "name LIKE '$printerName'" |
Invoke-CimMethod -MethodName printtestpage
if ($result.ReturnValue -eq 0)
{
"Test page printed on $printerName."
}
else
{
"Unable to print test page on $printerName."
"Error code $($result.ReturnValue)."
}
}

当这段代码运行时,将弹出一个对话框显示所有的打印机。请选择一个(或按住 CTRL 选择多个),将在选中的打印机上打印测试页。

PowerShell 技能连载 - 正确使用 FileSystemWatcher(第 2 部分)

在前一个技能中我们介绍了 FileSystemWatcher 并演示了当事件处理代码执行事件过长时会丢失文件系统改变事件。

要正确地使用 FileSystemWatcher,您需要异步地使用它并使用队列。这样即便脚本正忙于处理文件系统变更,也能够继续记录新的文件系统变更,并在一旦 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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# make sure you adjust this to point to the folder you want to monitor
$PathToMonitor = "c:\test"

explorer $PathToMonitor

$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
$FileSystemWatcher.Path = $PathToMonitor
$FileSystemWatcher.IncludeSubdirectories = $true

# make sure the watcher emits events
$FileSystemWatcher.EnableRaisingEvents = $true

# define the code that should execute when a file change is detected
$Action = {
$details = $event.SourceEventArgs
$Name = $details.Name
$FullPath = $details.FullPath
$OldFullPath = $details.OldFullPath
$OldName = $details.OldName
$ChangeType = $details.ChangeType
$Timestamp = $event.TimeGenerated
$text = "{0} was {1} at {2}" -f $FullPath, $ChangeType, $Timestamp
Write-Host ""
Write-Host $text -ForegroundColor Green

# you can also execute code based on change type here
switch ($ChangeType)
{
'Changed' { "CHANGE" }
'Created' { "CREATED"}
'Deleted' { "DELETED"
# uncomment the below to mimick a time intensive handler
<#
Write-Host "Deletion Handler Start" -ForegroundColor Gray
Start-Sleep -Seconds 4
Write-Host "Deletion Handler End" -ForegroundColor Gray
#>
}
'Renamed' {
# this executes only when a file was renamed
$text = "File {0} was renamed to {1}" -f $OldName, $Name
Write-Host $text -ForegroundColor Yellow
}
default { Write-Host $_ -ForegroundColor Red -BackgroundColor White }
}
}

# add event handlers
$handlers = . {
Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Changed -Action $Action -SourceIdentifier FSChange
Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Created -Action $Action -SourceIdentifier FSCreate
Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Deleted -Action $Action -SourceIdentifier FSDelete
Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Renamed -Action $Action -SourceIdentifier FSRename
}

Write-Host "Watching for changes to $PathToMonitor"

try
{
do
{
Wait-Event -Timeout 1
Write-Host "." -NoNewline

} while ($true)
}
finally
{
# this gets executed when user presses CTRL+C
# remove the event handlers
Unregister-Event -SourceIdentifier FSChange
Unregister-Event -SourceIdentifier FSCreate
Unregister-Event -SourceIdentifier FSDelete
Unregister-Event -SourceIdentifier FSRename
# remove background jobs
$handlers | Remove-Job
# remove filesystemwatcher
$FileSystemWatcher.EnableRaisingEvents = $false
$FileSystemWatcher.Dispose()
"Event Handler disabled."
}

当您运行这段代码时,将监控 $PathToMonitor 中定义的文件夹的改变,并且当变更发生时,会触发一条消息。当您按下 CTRL+C 时,脚本停止执行,并且所有事件处理器将在 finally 代码块中清理。

更重要的是:这段代码内部使用队列,所以当短时间内有大量修改发生时,它们将会等到 PowerShell 不忙碌的时候立即执行。您可以取消代码中的注释来演唱处理时间。现在,当一个文件删除后,处理器需要 4 秒钟的额外处理时间。

即便删除了大量文件,它们最终仍将显示出来。这里展示的方式比起前一个技能中基于 WaitForChanged() 的同步处理器更可靠。

PowerShell 技能连载 - 正确使用 FileSystemWatcher(第 1 部分)

FileSystemWatcher 可以监控一个文件或文件夹的改变。当有新文件复制到文件夹,或有文件删除或改变,您的 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
# make sure you adjust this
$PathToMonitor = "c:\test"


$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
$FileSystemWatcher.Path = $PathToMonitor
$FileSystemWatcher.IncludeSubdirectories = $true

Write-Host "Monitoring content of $PathToMonitor"
explorer $PathToMonitor
while ($true) {
$Change = $FileSystemWatcher.WaitForChanged('All', 1000)
if ($Change.TimedOut -eq $false)
{
# get information about the changes detected
Write-Host "Change detected:"
$Change | Out-Default

# uncomment this to see the issue
#Start-Sleep -Seconds 5
}
else
{
Write-Host "." -NoNewline
}
}

这个示例可以正常工作。当您向监控的文件夹增加文件,或者作出改变时,将会监测到改变的类型。您可以容易地得到该信息并采取操作。例如,对于 IT 部门,人们可以向一个投放文件夹投放文件和说明,您的脚本可以自动处理这些文件。

然而,这种方式又一个副作用:当监测到一个变更时,控制权返回到您的脚本,这样它可以处理这些变更。如果这时候另一个文件发生改变,而您的脚本并不是正在等待事件,那么将错过这个事件。您可以很容易地自我检查:

向变更发生时的执行代码中添加一些耗时的语句,例如 “Start-Sleep -Seconds 5“,然后对文件夹做多个改变。如您所见,检测到了第一个变更,但是 PowerShell 会持续五分钟忙碌,而其它改变事件都丢失了。

就在明天的技能中我们将确保您的 FileSystemWatcher 不会跳过任何变更!