PowerShell 技能连载 - 将文件路径转为 8.3 格式(第 2 部分)

在上一篇文章中,我们解释了如何使用旧的 COM 组件将默认的长路径名转换为短的 8.3 路径名。虽然可以偶尔进行转换,但使用 COM 组件的速度很慢且占用大量资源。

一种“清洁”的方法是直接使用 Windows API 调用。您可以通过以下方法访问将长文件路径转换为短文件路径的内部方法:

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
# this is the long path to convert
$path = "C:\Program Files\PowerShell\7\pwsh.exe"


# signature of internal API call
$signature = '[DllImport("kernel32.dll", SetLastError=true)]
public static extern int GetShortPathName(String pathName, StringBuilder shortName, int cbShortName);'
# turn signature into .NET type
$type = Add-Type -MemberDefinition $signature -Namespace Tools -Name Path -UsingNamespace System.Text

# create empty string builder with 300-character capacity
$sb = [System.Text.StringBuilder]::new(300)
# ask Windows to convert long path to short path with a max of 300 characters
$rv = [Tools.Path]::GetShortPathName($path, $sb, 300)

# output result
if ($rv -ne 0)
{
$shortPath = $sb.ToString()
}
else
{
$shortPath = $null
Write-Warning "Shoot. Could not convert $path"
}


"Short path: $shortPath"
评论

PowerShell 技能连载 - 将文件路径转为 8.3 格式(第 1 部分)

许多年前,文件和文件夹名称最多包含 8 个字符,而这些短路径名称仍然存在。它们甚至仍然有用:短路径名永远不会包含空格和其他特殊字符,因此永远不需要引号或转义。当路径变得很长时,短路径也可能会有所帮助。

但是,如何获得默认长路径名称的短路径名称呢?一种方法是使用 Windows 脚本宿主使用的旧 COM 组件:

1
2
3
4
5
6
7
8
9
10
11
# take any path
# in this example, I am taking the path where Powershell 7 is installed
# this requires that PowerShell 7 in fact is installed.
# You can use any other path as well
$path = (Get-Command -Name pwsh).Source

"Long path: $path"

# convert it to 8.3 short name
$shortPath = (New-Object -ComObject Scripting.FileSystemObject).GetFile($path).ShortPath
"Short path: $shortPath"

结果看起来像这样:

Long path: C:\Program Files\PowerShell\7\pwsh.exe
Short path: C:\PROGRA~1\POWERS~1\7\pwsh.exe
评论

PowerShell 技能连载 - 识别 PowerShell 宿主和路径

这是一个快速的单行代码,用于标识当前 PowerShell 宿主的完整路径:

1
2
PS> (Get-Process -Id $pid).Path
C:\Program Files\PowerShell\7\pwsh.exe

该路径会告诉您当前宿主的位置,并且您可以检查代码是否在 Windows PowerShell、PowerShell 7 或PowerShell ISE 中执行。

用类似的方法,您还可以按名称查找可执行文件的路径。例如,如果您想知道 PowerShell 7 在系统上的安装位置,请尝试以下操作:

1
2
PS C:\> (Get-Command -Name pwsh).Source
C:\Program Files\PowerShell\7\pwsh.exe

当然,如果找不到可执行文件,此行将产生错误。它必须位于 $env:path 中列出的文件夹之一中。

评论

PowerShell 技能连载 - 使用在线帮助(第 2 部分)

在上一个技能中,我们提到许多 PowerShell 用户更喜欢在线帮助,而不是本地下载的帮助。要默认使用联机帮助文档,请在新的 PowerShell 控制台中尝试以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# by default, -? opens LOCAL help
PS> dir -?

NAME
Get-ChildItem

SYNOPSIS
Gets the items and child items in one or more specified locations.


SYNTAX
...


# with this line you tell PowerShell to use the ONLINE help by default
PS> $PSDefaultParameterValues.Add("Get-Help:Online",$true)

# now, whenever you use -?, the ONLINE help opens in a nicely formatted browser window
PS> dir -?

使用 $PSDefaultParameterValues.Add("Get-Help:Online",$true) 命令告诉 PowerShell,Get-Help 命令应始终自动使用 -Online 参数,因此现在您始终可以获得基于浏览器的帮助。

尽管在大多数情况下这很好,但是联机帮助无法自动显示主题。如果需要显示有关主题的信息,只需使用 Get-Help 或带有 -ShowWindow 参数的帮助以显示本地帮助:

1
2
3
4
5
6
# this fails when help defaults to show ONLINE help
PS> help about_for
Get-Help : The online version of this Help topic cannot be displayed because the Internet address (URI) of the Help topic is not specified in the command code or in the help file for the command.

# the -ShowWindow parameter always shows local help in an extra window
PS C:\> help about_for -ShowWindow
评论

PowerShell 技能连载 - 使用在线帮助(第 1 部分)

PowerShell 支持本地帮助文件和联机资源。请看区别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# outputs help in same console window
# level of detail depends on whether local help was
# downloaded using Update-Help
PS C:\> help -Name Get-Process

NAME
Get-Process

SYNOPSIS
Gets the processes that are running on the local computer or a remote computer.


SYNTAX
Get-Process [[-Name] <String[]>] [-ComputerName <String[]>] [-FileVersionInfo] [-Module] []
...

# opens help in separate browser window
PS> help -Name Get-Process -Online

默认情况下,”help“(Get-Help 的别名)将帮助信息输出到 PowerShell 的输出窗口中。指定 -Online 开关参数时,浏览器会在单独的窗口中显示帮助文件。在线帮助文​​档采用了较好的格式,并且可以通过“复制”按钮轻松地复制和粘贴示例代码。并且由于联机文档是直接从 Microsoft 加载的,因此它们始终是最新的,因此无需通过 Update-Help 下载帮助。

这就是为什么许多用户喜欢在线帮助资源而不是本地帮助的原因。

线上还有许多有用的“关于”主题。关于主题涵盖了 PowerShell 语言和引擎的各个方面。您会在此处找到所有有关主题的很好的分类:https://docs.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about

评论

PowerShell 技能连载 - 在没有管理员特权的情况下更新帮助

在 Windows PowerShell 中,由于设计缺陷,更新帮助曾经需要管理员权限:帮助必须存储在模块所在的位置。更新 Windows 文件夹中存储的 Microsoft 模块的帮助需要对 Windows 文件夹的写权限。这就是普通用户无法下载和使用本地 PowerShell 帮助的原因。

在 PowerShell 7 中,此设计缺陷已得到纠正,现在可以将帮助安全地存储在用户配置文件中。不再需要涉及 PowerShell 模块的安装文件夹。

在 PowerShell 7 中使用 -Verbose 参数运行 Update-Help 以查看更改:

1
2
3
4
5
6
7
8
9
10
11
PS> Update-Help -Verbose
VERBOSE: Resolving URI: "https://go.microsoft.com/fwlink/?LinkId=717973"
VERBOSE: Your connection has been redirected to the following URI:
"https://pshelpprod.blob.core.windows.net/cabinets/powershell-5.1/"
VERBOSE: Performing the operation "Update-Help" on target "Microsoft.PowerShell.LocalAccounts, Current Version: 5.2.0.0, Available Version: 5.2.0.0, UICulture: en-US".
VERBOSE: Microsoft.PowerShell.LocalAccounts: Updated C:\Users\USERNAME\Dokumente\PowerShell\Help\Microsoft.PowerShell.LocalAccounts\1.0.0.0\en-US\Microsoft.Powershell.LocalAccounts.dll-Help.xml. Culture en-US Version 5.2.0.0
VERBOSE: Resolving URI: "https://go.microsoft.com/fwlink/?linkid=2113632"
VERBOSE: Your connection has been redirected to the following URI: "https://pshelp.blob.core.windows.net/powershell/help/7.0/Microsoft.PowerShell.Management/"
VERBOSE: Performing the operation "Update-Help" on target "Microsoft.PowerShell.Management, Current Version: 7.0.1.0, Available Version: 7.0.1.0, UICulture: en-US".
VERBOSE: Microsoft.PowerShell.Management: Updated C:\Users\USERNAME\Dokumente\PowerShell\Help\en-US\Microsoft.PowerShell.Commands.Management.dll-Help.xml. Culture en-US Version 7.0.1.0
...

要使用下载的本地帮助文件,您可以在想了解的命令后添加 “-?“ 通用参数:

1
2
3
4
5
6
7
8
PS> Get-Process -?

NAME
Get-Process

SYNOPSIS
Gets the processes that are running on the local computer or a remote computer.
...

如果您之前未下载本地帮助文件,则 “-“ 参数仅显示有限的语法帮助。

评论

PowerShell 技能连载 - PowerShell技能连载-检查配置文件脚本(第 2 部分)

在上一个脚本中,我们介绍了一一行代码,用于检查哪些配置文件脚本是存在的。但是,此解决方案仅只适用于单个宿主,因为每个宿主都使用自己的特定于宿主的配置文件路径。

这是一种更通用的方法:它列出了系统上存在的所有 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
# calculate the parent paths that can contain profile scripts
$Paths = @{
AllUser_WPS = $pshome
CurrentUser_WPS = Join-Path -Path ([Environment]::GetFolderPath('MyDocuments')) -ChildPath "WindowsPowerShell"
AllUser_PS = "$env:programfiles\PowerShell\*"
CurrentUser_PS = Join-Path -Path ([Environment]::GetFolderPath('MyDocuments')) -ChildPath "PowerShell"
}

# check all paths for PowerShell scripts ending on "profile.ps1"
$Paths.Keys | ForEach-Object {
$key = $_
$path = Join-Path -Path $paths[$key] -ChildPath '*profile.ps1'
Get-ChildItem -Path $Path |
ForEach-Object {
# create a custom object with all relevant details for any
# found profile script

# name of PowerShell host is the prefix of profile file name
if ($_.Name -like '*_*')
{
$hostname = $_.Name.Substring(0, $_.Name.Length-12)
}
else
{
$hostname = 'any'
}
[PSCustomObject]@{
# scope and PowerShell version is found in the
# name of the parent folder
Scope = $key.Split('_')[0]
PowerShell = $key.Split('_')[1]

Host = $hostname
Path = $_.FullName
}
}
}

结果报告了所有主机的现有 PowerShell 配置文件脚本,看起来可能与此类似:

Scope       PowerShell Host                    Path
-----       ---------- ----                    ----
CurrentUser WPS        Microsoft.PowerShellISE C:\Users\tobia\OneDrive\Dokumente\WindowsPowerShell\Microsoft.PowerShellISE_...
CurrentUser WPS        any                     C:\Users\tobia\OneDrive\Dokumente\WindowsPowerShell\profile.ps1
CurrentUser PS         Microsoft.VSCode        C:\Users\tobia\OneDrive\Dokumente\PowerShell\Microsoft.VSCode_profile.ps1
评论

PowerShell 技能连载 - PowerShell技能连载-检查配置文件脚本(第 1 部分)

PowerShell 最多使用四个配置文件脚本。当它们存在时,PowerShell 在启动时会静默执行所有内容。

重要的是要知道存在哪个配置文件脚本(如果有),因为它们的内容会减慢 PowerShell 的启动时间,并且恶意代码可以使用它们来偷偷运行。

手动测试配置文件路径可能很麻烦。这是一行有趣的代码,可以为您完成工作:

1
$profile.PSObject.Properties.Name | Where-Object { $_ -ne 'Length' } | ForEach-Object { [PSCustomObject]@{Profile=$_; Present=Test-Path $profile.$_; Path=$profile.$_}}

结果看起来像这样:

Profile                Present Path
-------                ------- ----
AllUsersAllHosts         False C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1
AllUsersCurrentHost      False C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShellISE_profile.ps1
CurrentUserAllHosts      False C:\Users\tobia\OneDrive\Dokumente\WindowsPowerShell\profile.ps1
CurrentUserCurrentHost    True C:\Users\tobia\OneDrive\Dokumente\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1
评论

PowerShell 技能连载 - 使用 Profile 脚本

配置文件脚本的工作方式类似于 PowerShell 中的自动启动脚本。它们不一定存在,但是如果存在,PowerShell 会在每次启动时静默执行其内容。最多有四个配置文件脚本,此行代码显示它们的路径:

1
2
3
4
5
6
7
PS> $profile | Select-Object -Property *

AllUsersAllHosts : C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1
AllUsersCurrentHost : C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShellISE_profile.ps1
CurrentUserAllHosts : C:\Users\tobia\OneDrive\Dokumente\WindowsPowerShell\profile.ps1
CurrentUserCurrentHost : C:\Users\tobia\OneDrive\Dokumente\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1
Length : 87

不必担心属性 “Length”:它是基于 $profile 是字符串这一事实的产物。PowerShell已在其中添加了四个属性,以返回受支持的配置文件的路径。

请注意,任何包含 “AllHosts” 的配置文件的路径对于所有 PowerShell 宿主都是相同的。您添加到其中的任何内容都可以在任何宿主中执行,包括 powershell.exe、PowerShell ISE、Visual Studio Code 或 PowerShell 7。包含 “CurrentHost” 的属性中的路径仅限于正在执行此行代码的宿主。

默认情况下,所有这些路径哪儿也不指向。要使用一个或多个,请确保创建了路径指向的文件。

评论

PowerShell 技能连载 - 识别用户 Profile

使用 $envuserprofile$home 创建用户文件的路径时要小心。当使用了 OneDrive,文档文件夹可能已重定向到名为“ OneDrive”的子文件夹。这里有些例子:

1
2
3
4
5
6
7
8
9
10
PS> $env:USERPROFILE
C:\Users\tobia

PS> $HOME
C:\Users\tobia

PS> $profile
C:\Users\tobia\OneDrive\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1

PS>

如您所见,PowerShell 配置文件脚本并不直接位于用户配置文件内的 Documents 文件夹中。而是将其改为名为 “OneDrive” 的子文件夹。

要查找当前的 Documents 文件夹,请改用 GetFolderPath()

1
2
PS> [Environment]::GetFolderPath('MyDocuments')
C:\Users\tobia\OneDrive\Documents

您甚至可以使用它来确定 OneDrive 是否重定向了用户文件:

1
2
$redirected = [Environment]::GetFolderPath('MyDocuments') -like '*\OneDrive\*'
$redirected

当 OneDrive 重定向文件夹时,此命令返回 $true,否则返回 $false

评论