PowerShell 技能连载 - Join-Path 遇上不存在的驱动器会失败

适用于 PowerShell 所有版本

您可能已经使用 Join-Path 通过父文件夹和文件来创建路径名。这个 cmdlet 在合并路径元素的时候能够正确地处理反斜杠的个数:

1
2
3
4
5
$part1 = 'C:\somefolder\'
$part2 = '\myfile.txt'
$result = Join-Path -Path $part1 -ChildPath $part2

$result

然而,如果路径元素存在,Join-Path 将会失败。所以您无法为一个没有加载的驱动器创建路径:

1
2
3
4
5
6
7
$part1 = 'L:\somefolder\'
$part2 = '\myfile.txt'
$result = Join-Path -Path $part1 -ChildPath $part2

$result

Join-Path : Cannot find drive. A drive with the name 'L' does not exist.

其实,Join-Path 所做的事也可以通过手工很好地完成。这段代码将合并两段路径元素并且能处理好反斜杠:

1
2
3
4
5
$part1 = 'L:\somefolder\'
$part2 = '\myfile.txt'
$result = $part1.TrimEnd('\') + '\' + $part2.TrimStart('\')

$result

如果您在 Windows 功能中启用了 Hyper-V 的 PowerShell 模块(如前一个技能中描述的一样的),您现在就可以通过 PowerShell 管理虚拟磁盘。

PowerShell 技能连载 - 用 Cmdlet 管理虚拟硬盘驱动器

适用于 Windows 8.1 Pro/Enterprise 或 Server 2012 R2

Windows 8.1 和 Server 2012 R2 带来一系列额外的 cmdlet 命令,有一部分用于管理虚拟磁盘。不过,在使用这些 cmdlet 之前,您需要先启用“Hyper-V 角色”(请注意需要 Windows 8.1 Pro 或 Enterprise 版才支持客户端的 Hyper-V。“Home”版则不支持该功能)。

在 Windows 8.1 中,您需要手动做以下操作:打开控制面板,进入程序/程序和功能。您也可以在 PowerShell 中键入“appwiz.cpl”打开该功能。

下一步,点击“启用或关闭 Windows 功能”。这将打开一个列出所有功能的对话框。请定位到“Hyper-V”节点并启用它。然后点击 OK。如果找不到“Hyper-V”节点,那么您的 Windows 版本不支持客户端的 Hyper-V。如果“Hyper-V 平台”选项呈灰色,那么您需要在计算机的 BIOS 设置中启用虚拟化支持。

安装该功能需要几秒钟。当安装完成以后,您就可以使用一系列新的 cmdlet:

1
2
3
4
5
6
7
8
9
10
PS> Get-Command -Module Hyper-V

CommandType Name ModuleName
----------- ---- ----------
Cmdlet Add-VMDvdDrive Hyper-V
Cmdlet Add-VMFibreChannelHba Hyper-V
Cmdlet Add-VMHardDiskDrive Hyper-V
Cmdlet Add-VMMigrationNetwork Hyper-V
Cmdlet Add-VMNetworkAdapter Hyper-V
(...)

PowerShell 技能连载 - 使用 IPv4 来 Ping

适用于 PowerShell 所有版本

您可以像其它命令一样在 PowerShell 脚本中使用 ping.exe。向 ping 命令加入“-4”参数之后,您可以强制 ping 命令使用 IPv4 协议(也可以用“-6”参数强制使用 IPv6)。

PS> ping localhost -4

PowerShell 技能连载 - 在 PowerShell ISE 中使用 F1 键

适用于 PowerShell ISE 3 及以上版本

当您下载了 PowerShell 帮助文件之后(在提升权限的 shell 中,用 Update-Help),您就可以用 Get-Help 命令来查找许多有用主题背后的信息。例如,以下代码将列出所有包含关键字“Parameter”的主题:

PS> Get-Help parameter

Name                              Category  Module                    Synopsis
----                              --------  ------                    --------
Get-ClusterParameter              Cmdlet    FailoverClusters          Get-Cl...
Set-ClusterParameter              Cmdlet    FailoverClusters          Set-Cl...
about_CommonParameters            HelpFile                            Descri...
about_Functions_Advanced_Param... HelpFile                            Explai...
about_Parameters                  HelpFile                            Descri...
about_Parameters_Default_Values   HelpFile                            Descri...
about_ActivityCommonParameters    HelpFile                            Descri...
about_WorkflowCommonParameters    HelpFile

一般性的帮助主题都是以“about_”开头的。

在 PowerShell ISE 中,只需要单击任何一个列出的名称,然后按下 F1 键,就可以在独立的帮主窗口中打开关联的主题。

PowerShell 技能连载 - 函数的优先级永远比 cmdlet 高

适用于 PowerShell 所有版本

函数的优先级永远比 cmdlet 高,所以如果两者名字相同,函数将会被执行。

这个函数将切实有效地改变 Get-Process 的行为:

function Get-Process
{
  'go away'
}

以下是意料之中的执行结果:

PS> Get-Process
go away

甚至如果您指定了 cmdlet 的完整限定名,函数也可以优先执行:

function Microsoft.PowerShell.Management\Get-Process
{
  'go away'
}

执行结果:

PS> Microsoft.PowerShell.Management\Get-Process -Id $pid
go away

这也适用于别名。它们的优先级甚至比函数更高。

唯一能确保确实执行的是 cmdlet 的方法是直接存取模块,选择希望执行的 cmdlet,然后直接调用它:

$module = Get-Module Microsoft.PowerShell.Management
$cmdlet = $module.ExportedCmdlets['Get-Process']
& $cmdlet

或者,只需要用 -noprofile 参数启动一个新的 PowerShell,确保没有人能混进您的 PowerShell 环境即可。

PowerShell 技能连载 - 用 EFS 加解密文件

适用于 PowerShell 所有版本

假设您的系统启用了 EFS (Encrypting File System),并且您想将文件保存到一个 NTFS 盘中,那么以下是加密以及确保只有您可以读取该文件的方法:

(Get-Item -Path 'C:\path..to..some..file.txt').Encrypt()

如果加密成功,那么在 explorer.exe 中将会显示为绿色而不是平常的黑色。用 Decrypt() 代替 Encrypt() 来撤销加密。

请注意需要事先设置了 EFS,并且您的公司可能需要一个集中存放的备份加密秘钥。

PowerShell 技能连载 - 用 Out-Host 代替 More

适用于 PowerShell 控制台

请注意本文所述的内容仅限于“真实的”控制台使用。它不适用于 PowerShell ISE 编辑器。

在 PowerShell 控制台中,许多用户在要分页输出数据时仍然采用管道输出到 more.com 的老办法:

PS> dir c:\windows | more

这看起来能用。不过当您输出大量数据到管道的时候,PowerShell 看起来卡住了:

PS> dir c:\windows -Recurse -ErrorAction SilentlyContinue | more

这是因为 more.com 无法实时工作。它会首先收集所有的输入数据,然后开始分页输出。

更好的办法是使用 Out-Host cmdlet,结合 -Paging 参数:

PS> dir c:\windows -Recurse -ErrorAction SilentlyContinue | Out-Host -Paging

它能即时输出结果,因为它一旦从管道接收到数据,就可以开始处理。

PowerShell 技能连载 - Invoke-Expression 是邪恶的

适用于 PowerShell 所有版本

请在您的脚本中避免使用 Invoke-Expression。这个 cmdlet 接受一个字符串,并且像一个命令一样执行它。在大多数情况下,这是没必要的,它只能带来风险。

以下是一个故意构造的例子:

function Test-BadBehavior($Path)
{
  Invoke-Expression "Get-ChildItem -Path $Path"
}

这个函数用 Invoke-Expression 来运行一个命令并且加上一个参数值,用来返回输入参数代表的路径下的文件列表。

由于 Invoke-Expression 接受任意的字符串参数,所以您将自己带入了类似“SQL 注入攻击”的环境中。请试着以这种方式运行脚本:

PS> Test-BadBehavior 'c:\;Get-Process'

这样写第二个命令也会被执行,并会列出所有运行中的进程。Invoke-Expression 常常被攻击者用于从外部 URL 下载恶意的程序并轻松地执行。

当然,Invoke-Expression 本来就没必要用。平时在生产系统的脚本中基本没什么用。请注意确保以硬编码的方式编写您想执行的命令:

function Test-BadBehavior($Path)
{
  Get-ChildItem -Path $Path
}

PowerShell 技能连载 - 显示 PowerShell 的命令行历史

适用于 PowerShell 所有版本

在 PowerShell 控制台(非 PowerShell ISE)中,您只要按下 F7 键就可以列出刚才键入的命令列表。当然,如果还没有执行过任何命令,就不会显示任何内容。

ALT+F7 键将会清空命令行历史列表。

PowerShell 技能连载 - 导入及安装证书

适用于 PowerShell 所有版本

若要以编程的方式从文件中加载证书并将它安装到证书管理其的指定位置,请看以下脚本:

$pfxpath = 'C:\temp\test.pfx'
$password = 'test'
[System.Security.Cryptography.X509Certificates.StoreLocation]$Store = 'CurrentUser'
$StoreName = 'root'

Add-Type -AssemblyName System.Security
$certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$certificate.Import($pfxpath, $password, 'Exportable')

$Store = New-Object system.security.cryptography.X509Certificates.x509Store($StoreName, $StoreLocation)
$Store.Open('ReadWrite')
$Store.Add($certificate)
$Store.Close()

您可以配置这个脚本并指定待导入的证书文件的路径和密码。您还可以指定其存储的位置(当前用户或本地计算机),以及将其放入的容器(例如“root”代表受信任的根证书颁发机构,“my”代表个人)。