PowerShell 技能连载 - 读取上次登录的用户和其他注册表值

使用 PowerShell 读取一些注册表值通常很容易:只需使用 Get-ItemProperty。此代码段读取 Windows 操作系统的才详细信息,例如:

1
2
3
4
$Path = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion"

Get-ItemProperty -Path $Path |
Select-Object -Property ProductName, CurrentBuild, ReleaseId, UBR

结果看起来像这样:

ProductName    CurrentBuild ReleaseId UBR
-----------    ------------ --------- ---
Windows 10 Pro 19042        2009      630

不幸的是,Get-ItemProperty 实际上得与 Select-Object 结合使用才理想,因为 cmdlet 总是会添加许多属性。如果您只想读取一个注册表值,即最后一个登录的用户,则始终需要将两者结合起来:

1
2
3
4
$Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI"

Get-ItemProperty -Path $Path |
Select-Object -ExpandProperty LastLoggedOnUser

另外,您可以将 Get-ItemProperty 的结果存储在变量中,并使用点号访问各个注册表值:

1
2
3
4
$Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI"

$values = Get-ItemProperty -Path $Path
$values.LastLoggedOnUser

如果此调用不能帮助您从其他用户帐户启动 Windows Terminal,那么在即将发布的技能中,我们将说明如何将应用程序转变为不再由 Windows 管理的便携式应用程序。取而代之的是,您可以用任何用户(包括高级帐户)运行它。敬请关注。

1
2
3
4
$Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI"

$key = Get-Item -Path $Path
$key.GetValue('LastLoggedOnUser')

PowerShell 技能连载 - 恒定函数

在 PowerShell中,您可以对函数进行写保护。当您这样做时,将无法在运行的 PowerShell 会话期间更改、覆盖或删除函数。尽管可能没有明显的作用。该方法如下:

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

"I have received $SomeParameter and could now process it..."
}

$null = New-Item -Path function:Invoke-Something -Options Constant,AllScope -Value $code

# run the function like this
Invoke-Something -SomeParameter "My Data"

由于该函数现在是恒定的,因此尝试重新定义它将会失败:

1
2
3
4
5
6
# you can no longer overwrite the function
# the following code raises an exception now
function Invoke-Something
{
# some new code
}

取消该效果的唯一方法是重新启动 PowerShell 会话。恒定变量是一个更有用的方案:通过将重要数据存储在写保护变量中,可以确保它们不会因意外或有意更改。此行定义了一个写保护变量 $testserver1,其中包含一些内容:

1
Set-Variable -Name testserver1 -Value server1 -Option Constant, AllScope

PowerShell 技能连载 - 禁止错误提示

使用 cmdlet,禁止错误提示似乎很容易:只需添加 –ErrorAction Ignore 参数。

但是,事实证明,这并不能消除所有错误。它仅禁止 cmdlet 选择处理的错误。特别是与安全相关的异常仍然显示。

如果要禁止所有错误提示,可以通过在命令尾部 2>$null 将异常传递给 null:

PS> Get-Service foobar 2>$null

这适用于所有命令甚至原生方法的调用。只需将代码括在大括号中,然后通过 调用运算符来进行调用:

1
2
3
4
5
6
PS> [Net.DNS]::GetHostEntry('notPresent')
MethodInvocationException: Exception calling "GetHostEntry" with "1" argument(s): "No such host is known."

PS> & {[Net.DNS]::GetHostEntry('notPresent')} 2>$null

PS>

PowerShell 技能连载 - 查找 PowerShell 宿主参数和可执行文件

PowerShell 宿主可以使用参数启动,即您可以使用 –NoProfile 之类的参数运行 powershell.exepwsh.exe,或提交要执行的脚本的路径。

在外壳中,您始终可以查看启动此外壳程序的命令,包括其他参数:

1
2
3
$exe, $parameters = [System.Environment]::GetCommandLineArgs()
"EXE: $exe"
"Args: $parameters"

当您使用 -NoProfile 启动 powershell.exe 时,结果如下所示:

EXE: C:\WINDOWS\system32\WindowsPowerShell\v1.0\PowerShell.exe Args: -noprofile

当您传入多个参数时,$parameters 是一个数组。

PowerShell 技能连载 - 管理已安装的模块(第 2 部分)

每当您通过 Install-Module 安装新模块或通过 Update-Module 更新现有模块时,新模块版本就会并行地安装。

如果您始终使用最新版本的模块,而无需访问特定的旧版本,则可能需要查找过时的模块并将其删除:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# get all installed modules as a hash table
# each key holds all versions of a given module
$list = Get-InstalledModule |
Get-InstalledModule -AllVersions |
Group-Object -Property Name -AsHashTable -AsString

# take all module names...
$list.Keys |
ForEach-Object {
# dump all present versions...
$list[$_] |
# sort by version descending (newest first)
Sort-Object -Property Version -Descending |
# and skip newest, returning all other
Select-Object -Skip 1
} |
# remove outdated (check whether you really don't need them anymore)
Uninstall-Module -WhatIf

PowerShell 技能连载 - 管理已安装的模块(第 1 部分)

通过 Install-Module 安装新的 PowerShell 模块时,PowerShell 会记住安装位置。因此,很容易获得通过 Install-Module 安装的模块的列表:

1
2
3
4
5
6
7
8
9
10
11
PS> Get-InstalledModule

Version Name Repository Description
------- ---- ---------- -----------
2.7.1.9 ISESteroids PSGallery Extension for PowerShell ISE 3.0 and better
2.2.0 PoSHue PSGallery Script and Control your Philips Hue Bridge, Lights, Gro...
0.14.0 platyPS PSGallery Generate PowerShell External Help files from Markdown
2.4 PSOneTools PSGallery commands taken from articles published at https://power...
1.0 QRCodeGenerator PSGallery Automatically creates QR codes as PNG images for person...
1.0 ScriptBlockLoggingAnalyzer PSGallery Functions to manage PowerShell script block logging
1.0 UserProfile PSGallery This module manages user profiles on local and remote c...

由于新版本是并行安装的,因此可以搜索不再需要的旧版本:

1
2
Get-InstalledModule |
Get-InstalledModule -AllVersions

PowerShell 技能连载 - 修复 PowerShell Gallery 的访问

The PowerShell Gallery (www.powershellgallery.com) 是查找新的 PowerShell 命令的理想场所。借助 Install-Module,您可以轻松下载并安装新的 PowerShell 模块。

但是,有时候会失败,有两个主要原因。

有时,Windows 10 自带的 PowerShellGet 模块已过时。然后,您会收到有关提示缺少参数或错误参数的异常信息。

要解决此问题,您需要手动更新 PowerShellGet。使用管理员权限运行以下行:

1
Install-Module -Name PowerShellGet -Repository PSGallery -Force

第二个常见原因:Windows 版本不支持 TLS 1.2 协议。在这种情况下,您会遇到连接问题。尝试运行以下命令:

1
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

此设置适用于当前的 PowerShell 会话。如果要保留此设置,请将该行放入您的配置文件脚本中。该路径可以在 $profile 中找到。

PowerShell 技能连载 - 创建图标

在上一个技能中,我们展示了如何微调 “Windows Terminal” 并将新的项目添加到可启动应用程序列表中。如果要为这些条目添加图标,则需要适当的图标文件。

这是一些从可执行文件中提取图标的 PowerShell 代码。您可以在 Windows Terminal 和其他地方使用生成的 ICO 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# create output folder
$destination = "c:\icons"
mkdir $destination -ErrorAction Ignore


Add-Type -AssemblyName System.Drawing

# extract PowerShell ISE icon
$path = "$env:windir\system32\windowspowershell\v1.0\powershell_ise.exe"
$name = "$destination\ise.ico"
[System.Drawing.Icon]::ExtractAssociatedIcon($path).ToBitmap().Save($name)

# extract Visual Studio Code icon
$Path = "$env:LOCALAPPDATA\Programs\Microsoft VS Code\code.exe"
$name = "$destination\vscode.ico"
[System.Drawing.Icon]::ExtractAssociatedIcon($path).ToBitmap().Save($name)

explorer $destination

PowerShell 技能连载 - 调优 Windows Terminal

在前面的技能中,我们介绍了如何通过 Microsoft Store 在 Windows 10 上安装 “Windows Terminal”。Windows Terminal 将 PowerShell 控制台放在单独的标签页中,非常实用。

您可以通过编辑设置文件来控制选项卡下拉列表中可用的控制台类型:在 Windows Terminal 中,在标题栏中单击向下箭头按钮,然后选择“设置”。这将在关联的编辑器中打开一个 JSON 文件。如果没有与 JSON 文件关联的编辑器,则可以选择一个或使用记事本。

“配置文件”部分列出了您可以使用“向下箭头”按钮在 Windows Terminal 中打开的控制台类型。这是一个例子:

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
"profiles":
    {
        "defaults":
        {
            // Put settings here that you want to apply to all profiles.
        },
        "list":
        [
            {
                // Make changes here to the powershell.exe profile.
                "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
                "name": "Windows PowerShell",
                "commandline": "powershell.exe",
                "hidden": false,
                "useAcrylic": true,
        "acrylicOpacity" : 0.8,
            },
            {
                // Make changes here to the cmd.exe profile.
                "guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
                "name": "Command Shell",
                "commandline": "cmd.exe",
                "hidden": false,
                "useAcrylic": true,
        "acrylicOpacity" : 0.8,
            },
            {
                "guid": "{574e775e-4f2a-5b96-ac1e-a2962a402336}",
                "hidden": false,
                "name": "PowerShell",
                "source": "Windows.Terminal.PowershellCore",
                "useAcrylic": true,
        "acrylicOpacity" : 0.8,
            },
            {
                "guid": "{2595cd9c-8f05-55ff-a1d4-93f3041ca67f}",
                "hidden": false,
                "name": "PowerShell Preview (msix)",
                "source": "Windows.Terminal.PowershellCore",
                "useAcrylic": true,
        "acrylicOpacity" : 0.8,
            },
            {
                "guid": "{b453ae62-4e3d-5e58-b989-0a998ec441b8}",
                "hidden": false,
                "name": "Azure Cloud Shell",
                "source": "Windows.Terminal.Azure",
                "useAcrylic": true,
        "acrylicOpacity" : 0.8,
            },
            {
                "guid": "{0caa0dae-35be-5f56-a8ff-afceeeaa6101}",
                "name": "Terminal As Admin",
                "commandline": "powershell.exe -command start 'C:/wt/wt.exe' -verb runas",
                "icon": "ms-appx:///Images/Square44x44Logo.targetsize-32.png",
                "hidden": false
            },
            {
                "guid": "{0ca30dae-35be-5f56-a8ff-afceeeaa6101}",
                "name": "ISE Editor",
                "commandline": "powershell.exe -command powershell_ise",
                "icon": "c:/wt/ise.ico",
                "hidden": false
            },
            {
                "guid": "{b39ac7db-ace0-4165-b312-5f2dfbbe4e4d}",
                "name": "VSCode",
                "commandline": "cmd.exe /c \"%LOCALAPPDATA%/Programs/Microsoft VS Code/code.exe\"",
                "icon": "c:/wt/vscode.ico",
                "hidden": false
            }
        ]
    }

如您所见,您可以定义任何可执行文件的路径,使用 “useAcrylic” 和 “acrylicOpacity” 等选项可以控制透明度。

看一下列表末尾的条目:它们说明了如何使用下拉菜单启动外部程序,例如编辑器(VSCode、ISE),甚至使用管理员权限打开 Windows Terminal。

诀窍是使用 “cmd.exe /c“ 或 “powershell“ 启动外部应用程序。如果直接启动外部应用程序,这将导致空白的选项卡窗口保持打开状态,直到再次关闭外部应用程序。

还要注意如何将喜欢的图标添加到列表项:使用 “icon” 并提交位图或 ico 文件。在即将发布的技巧中,我们将展示如何通过 PowerShell 代码创建此类图标文件。

如果您打算将新条目添加到列表中,请记住每个条目都需要一个唯一的 GUID。在 PowerShell 中,可以使用 New-Guid 来创建一个。

保存 JSON 文件后,修改立即生效。如果您进行的 JSON 编辑损坏了文件或引入了语法错误(例如不匹配的引号等),则 Windows Terminal 会报错。最好在编辑之前制作备份副本,并使用像 VSCode 这样的编辑器来支持 JSON 格式,并通过语法错误提示来帮助您。

PowerShell 技能连载 - 将 Windows Terminal 变成便携式应用程序

在 Windows 10 上,任何 PowerShell 用户都可以使用一个很棒的新工具:Windows Terminal。它使您可以同时使用多个 PowerShell 和其他控制台选项卡,并且可以混合使用 Windows PowerShell、PowerShell 7 和 Azure CloudShell 控制台。您可以从 Microsoft Store 安装 Windows Terminal。

与任何应用程序一样,Windows Terminal 由 Windows 管理,可以随时更新。而且它总是“按用户”安装。

如果计划在其中一个控制台中运行冗长的任务或关键业务脚本,则可能需要将该应用程序转换为仅由您自己控制的便携式应用程序。这样,多个用户也可以使用 Windows App。

以下脚本要求您已安装 Windows Terminal 应用程序,并且必须以管理员权限运行代码:

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
#requires -RunAsAdmin

# location to store portable app
$destination = 'c:\windowsterminal'


# search for installed apps...
Get-ChildItem "$env:programfiles\WindowsApps\" |
# pick Windows Terminal...
Where-Object name -like *windowsterminal* |
# find the executable...
Get-ChildItem -Filter wt.exe |
# identify executable versions...
Select-Object -ExpandProperty VersionInfo |
# sort versions...
Sort-Object -Property ProductVersion -Descending |
# pick the latest...
Select-Object -First 1 -ExpandProperty filename |
# get parent folder...
Split-Path |
# dump folder content...
Get-ChildItem |
# copy to destination folder
Copy-Item -Destination $destination -Force

# open folder
explorer $destination

# run portable app
Start-Process -FilePath "$destination\wt.exe"