PowerShell 技能连载 - 复制数组(第 1 部分)

当您复制变量内容时,您也可以只拷贝“引用”(内存地址),而不是内容。请看这个例子:

$a = 1..10
$b = $a
$b[0] = 'changed'
$b[0]
$a[0]

虽然您改变了 $b,但 $a 也跟着改变。两个变量都引用了相同的内存地址,所以两者具有相同的内容。

要创建一个数组的全新拷贝,您需要先对它进行克隆:

$a = 1..10
$b = $a.Clone()
$b[0] = 'changed'
$b[0]
$a[0]

PowerShell 技能连载 - 使用编码的脚本

在 VBScript 中有编码的脚本。编码并不是隐藏脚本内容的安全方法,但它能使用户获取代码内容略微更难一点。

以下是一个传入 PowerShell 脚本并对它编码的函数:

function ConvertTo-EncodedScript
{
  param
  (
    $Path,

    [Switch]$Open
  )

  $Code = Get-Content -Path $Path -Raw
  $Bytes = [System.Text.Encoding]::Unicode.GetBytes($Code)
  $Base64 = [Convert]::ToBase64String($Bytes)

  $NewPath = [System.IO.Path]::ChangeExtension($Path, '.pse1')
  $Base64 | Set-Content -Path $NewPath

  if ($Open) { notepad $NewPath }
}

编码后的脚本将会以 .pse1 扩展名来保存(这是一个完全随意定义的文件扩展名,并不是微软定义的)。

要执行这段编码后的脚本,请运行这段命令(不能在 PowerShell ISE 中运行):

powershell -encodedcommand (Get-Content 'Z:\pathtoscript\scriptname.pse1' -Raw)

请注意 PowerShell 最多支持大约 8000 个字符的编码命令。编码命令的本意是安全地将 PowerShell 代码传递给 powershell.exe,而不会被特殊字符打断命令行。

PowerShell 技能连载 - 用 try..finally 在 PowerShell 关闭时执行代码

如果您需要在 PowerShell 退出之前执行一些代码,您可以像这样简单地使用 try..finally 代码块:

try {
    # some code
    Start-Sleep -Seconds 20
} finally {
    # this gets executed even if the code in the try block throws an exception
    [Console]::Beep(4000,1000)
}

这段代码模拟一段长时间运行的脚本。甚至您关闭 PowerShell 窗口时,在 finally 块中的代码也会在 PowerShell 停止之前执行。

当然这得当脚本确实在运行时才有效。

PowerShell 技能连载 - 使用后台任务

后台任务可以用来加速您的脚本。如果您的脚本有一系列可并发执行的独立的任务组成,那么就合适使用后台任务。

后台任务适用于这两种情况:

  • 任务需要至少 3-4 秒执行时间。
  • 任务并不会返回大量数据。

以下是一个基本的由 3 个任务组成的后台任务场景。如果依次执行,它们一共约耗费 23 秒时间。通过使用后台任务,它们只消耗 11 秒(由最长的单个任务时间决定)加上一些额外的上下文时间。

#requires -Version 2

# three things you want to do in parallel
# for illustration, Start-Sleep is used
# remove Start-Sleep and replace with real-world
# tasks
$task1 = {
    Start-Sleep -Seconds 4
    dir $home
    }

$task2 = {
    Start-Sleep -Seconds 8
    Get-Service
}
$task3 = {
    Start-Sleep -Seconds 11
    'Hello Dude'
 }

$job1 = Start-Job -ScriptBlock $task1
$job2 = Start-Job -ScriptBlock $task2

$result3 = & $task3

Wait-Job -Job $job1, $job2

$result1 = Receive-Job -Job $job1
$result2 = Receive-Job -Job $job2

Remove-Job $job1, $job2

PowerShell 技能连载 - 移除 Windows 10 应用

Windows 10 预装了一系列应用程序。幸运的是,您可以用 PowerShell 来移除您不想要的程序。当然,需要管理员权限。

要查看安装了哪些应用程序,请运行这段代码:

Get-AppxPackage -User $env:USERNAME

这将列出所有用您自己的用户账户安装的应用程序。要移除应用程序,请使用 PackageFullName,并且将它传给 Remove-AppxPackage 命令。请在更改之前备份您的系统,而且风险自负。多数应用程序并不是必须的。

PowerShell 技能连载 - 修正远程发送者信息

如果您使用 Invoke-Command 来远程执行 PowerShell 代码,您可能会注意到 PowerShell 远程操作会添加一个新的 PSComputerName 属性用来表示数据的来源。

这段代码将获取名为 dc-01 的机器的进程列表。PSComputerName 属性指明了源计算机名。当您使用多于一台电脑时十分有用。

#requires -Version 2
$code = {
  Get-Process
}

Invoke-Command -ScriptBlock $code -ComputerName dc-01

然而,如果您将结果用管道输出到 Out-GridViewPSComputerName 属性消失了。

作为一个变通办法,当您将结果输出到 Select-Object 命令时,PSComputerName 属性将会在网格视图窗口中正确地显示。

#requires -Version 2
$code = {
  Get-Process |
    Select-Object -Property Name, ID, Handles, CPU
}

Invoke-Command -ScriptBlock $code -ComputerName dc-01 |
  Out-GridView

PowerShell 技能连载 - 设置新的 Windows 注册所有者

这一小段代码将提示输入新的注册所有者名,然后将更新 Windows 注册表中的值。请注意需要管理员权限。

#requires -RunAsAdministrator


$NewName = Read-Host -Prompt 'Enter New Registered Windows Owner'

Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name RegisteredOwner -Value $NewName -Type String

这也是一个更改 Windows 注册表的模板代码。

PowerShell 技能连载 - 下载文件

Invoke-WebRequest 可以从 internet 下载文件。这个例子将下载一个 33MB 的 NASA 公开视频到您的计算机上,然后用您计算机上 WMV 视频文件的关联应用打开它:

#requires -Version 3

$Video = 'http://s3.amazonaws.com/akamai.netstorage/HD_downloads/BEAMextract_final_revB.wmv'
$Destination = "$env:temp\nasavideo1.wmv"
Invoke-WebRequest -Uri $Video -OutFile $Destination -UseBasicParsing

Invoke-Item -Path $Destination

PowerShell 技能连载 - 将哈希表用作条件化的代码库

脚本中平时经常需要检测一个文件夹是否存在,如果不存在则创建:

#requires -Version 1

$path = 'c:\testfolder'
$exists = Test-Path -Path $path

if ($exists)
{
  $null = New-Item -Path $path -ItemType Directory
  Write-Warning -Message 'Folder created'
}
else
{
  Write-Warning -Message 'Folder already present'
}

以下是一种很不常见的方法,以一种不同的概念实现相同的功能:

#requires -Version 1

$Creator = @{
  $true = { Write-Warning 'Folder already present'}
  $false = {$null = New-Item -Path $Path -ItemType Directory
            Write-Warning 'Folder created'}
}


$Path = 'c:\testfolder2'
& $Creator[(Test-Path $Path)]

实际上,这段脚本用了一个哈希表($Creator))来存储两个脚本块,而它的键是 $true$false

现在,根据文件夹存在与否,哈希表将返回合适的脚本块,接下来被 &(调用)操作符执行。

哈希表用这种方式来模拟从句。

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

Invoke-Expression 接受任何字符串输入并将它视为 PowerShell 代码。通过这种方式,您可以动态地创建代码,并执行它。

Invoke-Expression 是一个非常危险的命令,因为不仅您可以创建动态的代码。恶意的脚本可以隐藏它的邪恶目的,例如通过 web 站点下载代码。

以下是一个安全并有趣的例子,演示了如何从下载到执行一段代码:

#requires -Version 3

Invoke-Expression -Command (Invoke-WebRequest -Uri 'http://bit.ly/e0Mw9w' -UseBasicParsing).Content

如果您不希望发生意外,这行代码可以帮您预览将会发生什么。请在 PowerShell ISE 中运行这段代码。它将显示从 internet 下载的 PowerShell 代码,而不是立即执行它:

#requires -Version 3

$file = $psise.CurrentPowerShellTab.Files.Add()

$file.Editor.text = (Invoke-WebRequest -Uri 'http://bit.ly/e0Mw9w' -UseBasicParsing).Content