PowerShell 技能连载 - 向上下文菜单添加个人 PowerShell 命令

您可以针对文件类型,例如 PowerShell 文件,添加个人的上下文菜单。当您右键单击一个 .ps1 文件时,将显示这些上下文菜单命令。它们关联到个人账户,并且不需要管理员权限就可以设置。

以下是一个实现的脚本。只需要调整头两个变量:指定上下文菜单中需要出现的命令,以及需要执行的命令行。在这个命令中,使用 “%1“ 作为右键单击时 PowerShell 脚本路径的占位符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# specify your command name
$ContextCommand = "Open Script with Notepad"
# specify the command to execute. "%1" represents the file path to your
# PowerShell script
$command = 'notepad "%1"'


$baseKey = 'Registry::HKEY_CLASSES_ROOT\.ps1'
$id = (Get-ItemProperty $baseKey).'(Default)'
$ownId = $ContextCommand.Replace(' ','')
$contextKey = "HKCU:\Software\Classes\$id\Shell\$ownId"
$commandKey = "$ContextKey\Command"

New-Item -Path $commandKey -Value $command -Force
Set-Item -Path $contextKey -Value $ContextCommand

当您运行这段脚本时,将生成一个名为 “Open Script with Notepad” 的新的上下文菜单命令。您可以利用这个钩子并且设计任何命令,包括 GitHub 或备份脚本。

请注意:当您对 OpenWith 打开方式选择了一个非缺省的命令,那么自定义命令将不会在上下文菜单中显示。这个命令仅当记事本为缺省的 OpenWith 打开方式应用时才出现。

要移除所有上下文菜单扩展,请运行以下代码:

1
2
3
4
$baseKey = 'Registry::HKEY_CLASSES_ROOT\.ps1'
$id = (Get-ItemProperty $baseKey).'(Default)'
$contextKey = "HKCU:\Software\Classes\$id"
Remove-Item -Path $contextKey -Recurse -Force

PowerShell 技能连载 - 用 PowerShell 锁定屏幕

以下是一个名为 Lock-Screen 的 PowerShell 函数,它可以锁定屏幕,禁止用户操作。可以指定一个自定义消息,并且可以在锁定时将屏幕调暗。

以下是一个调用示例:

1
PS> Lock-Screen -LockSeconds 4 -DimScreen -Title 'Go away and come back in {0} seconds.'

以下是 Lock-Screen 的源码:

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
Function Lock-Screen
{
[CmdletBinding()]
param
(
# number of seconds to lock
[int]
$LockSeconds = 10,

# message shown. Use {0} to insert remaining seconds
# do not use {0} for a static message
[string]
$Title = 'wait for {0} more seconds...',

# dim screen
[Switch]
$DimScreen
)

# when run without administrator privileges, the keyboard will not be blocked!

# get access to API functions that block user input
# blocking of keyboard input requires admin privileges
$code = @'
[DllImport("user32.dll")]
public static extern int ShowCursor(bool bShow);

[DllImport("user32.dll")]
public static extern bool BlockInput(bool fBlockIt);
'@

$userInput = Add-Type -MemberDefinition $code -Name Blocker -Namespace UserInput -PassThru

# get access to UI functionality
Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName PresentationCore

# set window opacity
$opacity = 1
if ($DimScreen) { $opacity = 200 }

# create a message label
$label = New-Object -TypeName Windows.Controls.Label
$label.FontSize = 60
$label.FontFamily = 'Consolas'
$label.FontWeight = 'Bold'
$label.Background = 'Transparent'
$label.Foreground = 'Blue'
$label.VerticalAlignment = 'Center'
$label.HorizontalAlignment = 'Center'


# create a window
$window = New-Object -TypeName Windows.Window
$window.WindowStyle = 'None'
$window.AllowsTransparency = $true
$color = [Windows.Media.Color]::FromArgb($opacity, 0,0,0)
$window.Background = [Windows.Media.SolidColorBrush]::new($color)
$window.Opacity = 0.8
$window.Left = $window.Top = 0
$window.WindowState = 'Maximized'
$window.Topmost = $true
$window.Content = $label

# block user input
$null = $userInput::BlockInput($true)
$null = $userInput::ShowCursor($false)

# show window and display message
$null = $window.Dispatcher.Invoke{
$window.Show()
$LockSeconds..1 | ForEach-Object {
$label.Content = ($title -f $_)
$label.Dispatcher.Invoke([Action]{}, 'Background')
Start-Sleep -Seconds 1
}
$window.Close()
}

# unblock user input
$null = $userInput::ShowCursor($true)
$null = $userInput::BlockInput($false)
}

请注意 Lock-Screen 需要管理员权限才能完全禁止用户输入。

PowerShell 技能连载 - 禁止用户输入

如果一个 PowerShell 脚本需要进行危险的操作,而且用户操作必须被禁止,那么您可以使用 API 来临时禁止所有键盘输入。锁定键盘输入不需要管理员权限。

以下是一个演示如何阻止所有键盘输入 4 秒钟的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#requires -RunAsAdministrator
# when run without administrator privileges, the keyboard will not be blocked!

# get access to API functions that block user input
# blocking of keyboard input requires administrator privileges
$code = @'
[DllImport("user32.dll")]
public static extern bool BlockInput(bool fBlockIt);
'@

$userInput = Add-Type -MemberDefinition $code -Name Blocker -Namespace UserInput -PassThru

# block user input
$null = $userInput::BlockInput($true)

Write-Warning "Your input has been disabled for 4 seconds..."
Start-Sleep -Seconds 4

# unblock user input
$null = $userInput::BlockInput($false)

PowerShell 技能连载 - 向编码的命令传递参数

对 PowerShell 代码编码是一种在 PowerShell 环境之外运行 PowerShell 代码的方法,例如在批处理文件中。以下是一些读取 PowerShell 代码,对它编码,并且通过命令行执行它的示例代码:

1
2
3
4
5
6
7
8
9
10
$command = {
Get-Service |
Where-Object Status -eq Running |
Out-GridView -Title 'Pick a service that you want to stop' -PassThru |
Stop-Service
}

$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$encodedCommand = [Convert]::ToBase64String($bytes)
"powershell.exe -noprofile -encodedcommand $encodedCommand" | clip

当执行这段代码之后,您会发现剪贴板里有 PowerShell 命令。代码类似这样:

1
powershell.exe -noprofile -encodedcommand DQAKAEcAZQB0AC0AUwBlAHIAdgBpAGMAZQAgAHwAIAANAAoAIAAgACAAIABXAGgAZQByAGUALQBPAGIAagBlAGMAdAAgAFMAdABhAHQAdQBzACAALQBlAHEAIABSAHUAbgBuAGkAbgBnACAAfAAgAA0ACgAgACAAIAAgAE8AdQB0AC0ARwByAGkAZABWAGkAZQB3ACAALQBUAGkAdABsAGUAIAAnAFAAaQBjAGsAIABhACAAcwBlAHIAdgBpAGMAZQAgAHQAaABhAHQAIAB5AG8AdQAgAHcAYQBuAHQAIAB0AG8AIABzAHQAbwBwACcAIAAtAFAAYQBzAHMAVABoAHIAdQAgAHwAIAANAAoAIAAgACAAIABTAHQAbwBwAC0AUwBlAHIAdgBpAGMAZQANAAoA

当您打开一个新的 cmd.exe 窗口,您可以将这段代码粘贴到控制台并且执行纯的 PowerShell 代码。您可以在任何有足够空间容纳整行代码的地方执行编码过的命令。因为长度限制,编码过的命令在快捷方式文件(.lnk 文件)以及开始菜单中的运行对话框中工作不正常。

还有一个额外的限制:无法传递参数到编码过的命令。除非使用一个很酷的技能。首先,在代码中加入一个 param() 块,然后使该参数成为必选。然后,从一个外部的 PowerShell 通过管道将参数传递进去。

以下是一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$command = {
param
(
[Parameter(Mandatory)]
[string]
$FirstName,

[Parameter(Mandatory)]
[string]
$LastName
)
"Hello, your first name is $FirstName and your last name is $lastname!"
}

$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$encodedCommand = [Convert]::ToBase64String($bytes)
"powershell.exe -noprofile -command 'Tobias', 'Weltner' | powershell -noprofile -encodedcommand $encodedCommand" | clip

命令看起来类似这样:

1
powershell.exe -noprofile -command 'Tom', 'Tester' | powershell -noprofile -encodedcommand DQAKAHAAYQByAGEAbQANAAoAKAANAAoAIAAgACAAIABbAFAAYQByAGEAbQBlAHQAZQByACgATQBhAG4AZABhAHQAbwByAHkAKQBdAA0ACgAgACAAIAAgAFsAcwB0AHIAaQBuAGcAXQANAAoAIAAgACAAIAAkAEYAaQByAHMAdABOAGEAbQBlACwADQAKAA0ACgAgACAAIAAgAFsAUABhAHIAYQBtAGUAdABlAHIAKABNAGEAbgBkAGEAdABvAHIAeQApAF0ADQAKACAAIAAgACAAWwBzAHQAcgBpAG4AZwBdAA0ACgAgACAAIAAgACQATABhAHMAdABOAGEAbQBlAA0ACgApAA0ACgAiAEgAZQBsAGwAbwAsACAAeQBvAHUAcgAgAGYAaQByAHMAdAAgAG4AYQBtAGUAIABpAHMAIAAkAEYAaQByAHMAdABOAGEAbQBlACAAYQBuAGQAIAB5AG8AdQByACAAbABhAHMAdAAgAG4AYQBtAGUAIABpAHMAIAAkAGwAYQBzAHQAbgBhAG0AZQAhACIADQAKAA==

当您运行这段代码,参数 “Tom” 和 “Tester” 将通过管道传递给执行编码过命令的 PowerShell。由于参数是必选的,所以管道的元素会传递给提示符,并且被编码的命令处理。

PowerShell 技能连载 - 删除无法删除的注册表键

删除注册表键通常很简单,用 Remove-Item 就可以了。然而,有时你会遇到一些无法删除的注册表键。在这个技能中我们将演示一个例子,并且提供一个解决方案。

在前一个技能中我们解释了当定义了一个非缺省的打开方式之后,PowerShell 文件的“使用 PowerShell 运行”上下文命令可能会丢失,而且出现了这个注册表键:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1\UserChoice

当您在文件管理器中右键点击一个 PowerShell 脚本,然后通过“选择其他应用”,选择一个 ISE 或 VSCode 之外的非缺省应用,并选中“始终使用此应用打开 .ps1 文件”复选框,来使用其他应用来打开 PowerShell 文件,那么注册表中就会创建上述注册表键。

当上述注册表键存在时,上下文菜单中缺省的 PowerShell 命令,例如“使用 PowerShell 运行”将不可见。用注册表删除这个键修复它很容易,但出于某种未知原因用 PowerShell 命令来删除会失败。所有 .NET 的方法也会失败。

以下命令会执行失败:

1
Remove-Item -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1\UserChoice

以下命令也会执行失败(这是 .NET 的等价代码):

1
2
3
$parent = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey('Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1', $true)
$parent.DeleteSubKeyTree('UserChoice',$true)
$parent.Close()

要删除这个键,您需要显示地调用 DeleteSubKey() 方法来代替 DeleteSubKeyTree()。明显地,在那个键中有一些不可见的异常子键导致该键无法删除。

当您只是删除该键(不包含它的子键,虽然子键并不存在),该键可以正常删除,并且 PowerShell 的“使用 PowerShell 运行“命令就恢复了:

1
2
3
$parent = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey('Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1', $true)
$parent.DeleteSubKey('UserChoice', $true)
$parent.Close()

另一方面:由于这段代码只操作了用户配置单元,所以不需要任何特权,然而在 regedit.exe 中修复则需要管理员特权。

PowerShell 技能连载 - 修复 PowerShell 上下文菜单

当您在文件管理器中右键点击一个 PowerShell 脚本文件时,通常会见到一个名为“使用 PowerShell 运行”的上下文菜单项,可以通过它快速地执行 PowerShell 脚本。

然而,在某些系统中,“使用 PowerShell 运行“命令缺失了。原因是当您定义了一个非缺省的“打开方式”命令,那么该命令就会隐藏。要修复它,您只需要删除这个注册表键:

1
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1\UserChoice

在 regedit.exe 中删除这个键很简单,而且当这个键移除之后,“使用 PowerShell 运行”上下文菜单就再次可见了。

不过实际中在 PowerShell 删除这个注册表键却不太容易。以下这些命令执行都会失败,报告某些子项无法删除:

1
2
3
4
5
6
7
8
9
PS C:\> Remove-Item HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1\UserChoice

PS C:\> Remove-Item Registry::HKEY:CURRENT_USER:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1\UserChoice

PS C:\> Remove-Item 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1\UserChoice'

PS C:\> Remove-Item HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\'.ps1'\UserChoice

PS C:\> Remove-Item HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1\UserChoice -Recurse -Force

我们明天会提供一个解决方案来应对这种情况。

PowerShell 技能连载 - 让新手运行 PowerShell 脚本

假设您要把一段 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
# specify the path to your PowerShell script
$ScriptPath = "C:\test\test.ps1"

# create a lnk file
$shortcutPath = [System.IO.Path]::ChangeExtension($ScriptPath, "lnk")
$filename = [System.IO.Path]::GetFileName($ScriptPath)

# create a new shortcut
$shell = New-Object -ComObject WScript.Shell
$scut = $shell.CreateShortcut($shortcutPath)
# launch the script with powershell.exe:
$scut.TargetPath = "powershell.exe"
# skip profile scripts and enable execution policy for this one call
# IMPORTANT: specify only the script file name, not the complete path
$scut.Arguments = "-noprofile -executionpolicy bypass -file ""$filename"""
# IMPORTANT: leave the working directory empty. This way, the
# shortcut uses relative paths
$scut.WorkingDirectory = ""
# optinally specify a nice icon
$scut.IconLocation = "$env:windir\system32\shell32.dll,162"
# save shortcut file
$scut.Save()

# open shortcut file in File Explorer
explorer.exe "/select,$shortcutPath"

这个快捷方式就放在您的 PowerShell 脚本相邻的位置。它使用相对路径,由于我们保证快捷方式和 PowerShell 脚本放在相同的路径,所以它能够完美地工作——这样您可以将两个文件打包,然后将它们发送给客户。当他解压了文件,快捷方式仍然可以工作。您甚至可以将快捷方式改为想要的名字,例如“双击我执行”。

重要:快捷方式使用相对路径来确保这个解决方案便携化。如果您将快捷方式移动到脚本之外的文件夹,那么该快捷方式显然不能工作。。

PowerShell 技能连载 - 修复 PowerShellGet 发布

如果您在使用 Publish-Module 来将您的模块发布到 PowerShell 仓库,并且您一直收到不支持的命令的错误信息,那么可能需要重新安装管理模块上传的可执行程序。当这些可执行程序太旧时,它们可能不能再能和最新的 PowerShellGet 模块同步。

运行这段代码,以管理员权限(对所有用户有效)下载并更新 nuget.exe:

1
2
3
4
5
6
7
$Path = "$env:ProgramData\Microsoft\Windows\PowerShell\PowerShellGet"
$exists = Test-Path -Path $Path
if (!$exists)
{
$null = New-Item -Path $Path -ItemType Directory
}
Invoke-WebRequest -Uri https://aka.ms/psget-nugetexe -OutFile "$Path\NuGet.exe"

运行这段代码只针对当前用户下载并安装 nuget.exe:

1
2
3
4
5
6
7
$Path = "$env:LOCALAPPDATA\Microsoft\Windows\PowerShell\PowerShellGet"
$exists = Test-Path -Path $Path
if (!$exists)
{
$null = New-Item -Path $Path -ItemType Directory
}
Invoke-WebRequest -Uri https://aka.ms/psget-nugetexe -OutFile "$Path\NuGet.exe"

PowerShell 技能连载 - 将大文件拆分成小片段(第 3 部分)

在前一个技能中我们延时了如何使用 PowerShell 将文件分割成小的分片,以及如何将这些分片合并起来,重建原始文件。我们甚至进一步扩展了这些函数,将它们发布到 PowerShell Gallery。所以要分割和合并文件,只需要获取该模块并像这样安装:

1
PS> Install-Module -Name FileSplitter -Repository PSGallery -Scope CurrentUser -Force

现在当您需要将一个大文件分割成多个小片时,只需要运行以下代码:

1
2
3
4
5
6
7
PS C:\> Split-File -Path 'C:\movies\Woman tries putting gas in a Tesla.mp4' -PartSizeBytes 10MB -AddSelfExtractor -Verbose
VERBOSE: saving to C:\users\tobwe\Downloads\Woman tries putting gas in a Tesla.mp4.0.part...
VERBOSE: saving to C:\users\tobwe\Downloads\Woman tries putting gas in a Tesla.mp4.1.part...
VERBOSE: saving to C:\users\tobwe\Downloads\Woman tries putting gas in a Tesla.mp4.2.part...
VERBOSE: Adding extractor scripts...

PS C:\

Split-Path 将文件分割成不超过 PartSizeByte 参数指定的大小。感谢 -AddSelfExtractor,它还添加了一个可以将分片文件合并为原始文件的脚本,以及一个双击即可执行合并操作的快捷方式。以下是您获得的文件::

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS C:\users\tobwe\Downloads> dir *gas*


Folder: C:\movies


Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 03.03.2019 18:11 2004 Extract Woman tries putting gas in a Tesla.mp4.lnk
-a---- 03.03.2019 16:54 24081750 Woman tries putting gas in a Tesla.mp4
-a---- 03.03.2019 18:11 10485760 Woman tries putting gas in a Tesla.mp4.0.part
-a---- 03.03.2019 18:11 10485760 Woman tries putting gas in a Tesla.mp4.1.part
-a---- 03.03.2019 18:11 3110230 Woman tries putting gas in a Tesla.mp4.2.part
-a---- 03.03.2019 18:11 3179 Woman tries putting gas in a Tesla.mp4.3.part.ps1

如您所见,有许多包含 .part 扩展名的文件,以及一个扩展名为 .part.ps1 的文件。后者是合并脚本。当您运行这个脚本时,它读取这些分片文件并重建原始文件,然后将删除所有分片文件以及自身。最终,该合并脚本将打开文件管理器并选中恢复的文件。

由于对于普通用户来说可能不了解如何运行 PowerShell 脚本,所以还有一个额外的名为 “Extract…”,扩展名为 .lnk 的文件。这是一个快捷方式文件。当用户双击这个文件,它将运行 PowerShell 合并脚本并恢复原始文件。

如果您希望手工恢复原始文件,您可以手工调用 Join-File

1
2
3
4
5
6
7
PS> Join-File -Path 'C:\users\tobwe\Downloads\Woman tries putting gas in a Tesla.mp4' -Verbose -DeletePartFiles
VERBOSE: processing C:\users\tobwe\Downloads\Woman tries putting gas in a Tesla.mp4.0.part...
VERBOSE: processing C:\users\tobwe\Downloads\Woman tries putting gas in a Tesla.mp4.1.part...
VERBOSE: processing C:\users\tobwe\Downloads\Woman tries putting gas in a Tesla.mp4.2.part...
VERBOSE: Deleting part files...

PS>

PowerShell 技能连载 - 将大文件拆分成小片段(第 2 部分)

在前一个技能中我们介绍了如何讲一个大文件分割成小块。今天,我们将完成一个函数,它能将这些小文件合并成原来的文件。

假设您已经按上一个技能用 Split-File 将一个大文件分割成多个小文件。现在拥有了一大堆扩展名为 “.part” 的文件。这是上一个技能的执行结果:

1
2
3
4
5
6
7
8
9
10
11
12
PS> dir "C:\Users\tobwe\Downloads\*.part"


Folder: C:\Users\tobwe\Downloads


Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 03.03.2019 16:25 6291456 Woman tries putting gas in a Tesla.mp4.00.part
-a---- 03.03.2019 16:25 6291456 Woman tries putting gas in a Tesla.mp4.01.part
-a---- 03.03.2019 16:25 6291456 Woman tries putting gas in a Tesla.mp4.02.part
-a---- 03.03.2019 16:25 5207382 Woman tries putting gas in a Tesla.mp4.03.part

要合并这些部分,请使用我们新的 Join-File 函数(不要和内置的 Join-Path 命令混淆)。让我们先看看它是如何工作的:

1
2
3
4
5
6
7
8
PS C:\> Join-File -Path "C:\Users\tobwe\Downloads\Woman tries putting gas in a Tesla.mp4" -DeletePartFiles -Verbose
VERBOSE: processing C:\Users\tobwe\Downloads\Woman tries putting gas in a Tesla.mp4.00.part...
VERBOSE: processing C:\Users\tobwe\Downloads\Woman tries putting gas in a Tesla.mp4.01.part...
VERBOSE: processing C:\Users\tobwe\Downloads\Woman tries putting gas in a Tesla.mp4.02.part...
VERBOSE: processing C:\Users\tobwe\Downloads\Woman tries putting gas in a Tesla.mp4.03.part...
VERBOSE: Deleting part files...

PS C:\>

只需要提交文件名(不需要分片编号和分片扩展名)。当您指定了 -DeletePartFiles 参数,函数将会在创建完原始文件之后删除分片文件。

要使用 Join-File 函数,需要先运行这段代码:

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
function Join-File
{

param
(
[Parameter(Mandatory)]
[String]
$Path,

[Switch]
$DeletePartFiles
)

try
{
# get the file parts
$files = Get-ChildItem -Path "$Path.*.part" |
# sort by part
Sort-Object -Property {
# get the part number which is the "extension" of the
# file name without extension
$baseName = [IO.Path]::GetFileNameWithoutExtension($_.Name)
$part = [IO.Path]::GetExtension($baseName)
if ($part -ne $null -and $part -ne '')
{
$part = $part.Substring(1)
}
[int]$part
}
# append part content to file
$writer = [IO.File]::OpenWrite($Path)
$files |
ForEach-Object {
Write-Verbose "processing $_..."
$bytes = [IO.File]::ReadAllBytes($_)
$writer.Write($bytes, 0, $bytes.Length)
}
$writer.Close()

if ($DeletePartFiles)
{
Write-Verbose "Deleting part files..."
$files | Remove-Item
}
}
catch
{
throw "Unable to join part files: $_"
}
}

今日知识点:

  • 使用 [IO.Path] 类来分割文件路径
  • 使用 [IO.file] 类以字节的方式存取文件内容
  • 使用 OpenWrite() 以字节的方式写入文件