PowerShell 技能连载 - 加密文本

有很多办法可以加密文本。以下是一个未使用“密钥”的方法,所谓的密钥实际上是您的用户标识符加上机器名。

当您使用 ConvertTo-TextEncrypted 命令加密文本时,结果只能由同一个人在同一台机器上使用 ConvertFrom-TextEncrypted 命令来解密:

#requires -Version 2


function ConvertTo-TextEncrypted
{
    param([Parameter(ValueFromPipeline = $true)]$Text)

    process
    {
        $Text |
        ConvertTo-SecureString -AsPlainText -Force |
        ConvertFrom-SecureString
    }
}


function ConvertFrom-TextEncrypted
{
    param([Parameter(ValueFromPipeline = $true)]$Text)

    process
    {
        $SecureString = $Text |
        ConvertTo-SecureString

        $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString)
        [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
    }
}

要测试效果,请先使用这行代码:

PS> "Hello World" | ConvertTo-TextEncrypted | ConvertFrom-TextEncrypted
Hello World

下一步,得到相同的密文,然后将它保存到一个文件中:

$Path = "$env:temp\secret.txt"
'Hello World' | ConvertTo-TextEncrypted | Set-Content -Path $Path

现在,使用这行代码来读取加密的文件,然后对它进行解密:

$Path = "$env:temp\secret.txt"
Get-Content -Path $Path | ConvertFrom-TextEncrypted

请注意两个脚本都不包含密码。而实际的密码正是你的用户标识符。当另一个人,或是您在另一台机器上试图解开文件中的密文,将会失败。

这个方法可以用来安全地保存个人密码,这样您就不用每天都输入了。

PowerShell 技能连载 - 将过期的日志存档

有时,你会需要在文件超过一定的日期之后将它们移动到一个存档文件夹。

这个例子演示了如何检测过期文件的基本策略,以及如何将它们移动到存档文件夹:

#requires -Version 1
# how old (in days) would obsolete files be
$Days = 14

# where to look for obsolete files
$Path = $env:windir
$Filter = '*.log'

# where to move obsolete files
$DestinationPath = 'c:\archive'

# make sure destination folder exists
$destinationExists = Test-Path -Path $DestinationPath
if (!$destinationExists)
{
    $null = New-Item -Path $DestinationPath -ItemType Directory
}

$cutoffDate = (Get-Date).AddDays(-$Days)

Get-ChildItem -Path $Path -Filter $Filter -Recurse -ErrorAction SilentlyContinue |
Where-Object -FilterScript {
    $_.LastWriteTime -lt $cutoffDate
} |
Move-Item -Destination c:\archive -WhatIf

这个示例脚本在 Windows 文件夹和它的子文件夹中查找所有扩展名为 *.log 的日志文件。所有超过 14 天(在过去 14 天内没有修改过)的日志文件将被移动到 c:\archive 目录中。如果该文件夹不存在,则会自动创建。

请注意这只是一个示例脚本。您可能需要管理员权限才能确实将文件移出 Windows 文件夹。

PowerShell 技能连载 - 创建友好的“结束进程”应用程序

在前一个技能中,我们展示了如何选择应用程序并且立即结束它们。所有未保存的数据将会丢失。

这是一个更复杂的实现。它可以列出所有的应用程序,您可以选择希望结束的应用程序(按住 CTRL 键选择多个应用程序)。

该脚本接下来尝试向选中的应用程序发送“关闭窗口”消息。用户将有机会保存未保存的数据。用户也可以取消“关闭窗口”消息。

This is why the script waits for 15 seconds, and then checks whether the requested applications actually ended. If not, these applications are killed immediately (remove –WhatIf to actually kill them):
这是为什么该脚本等待 15 秒,然后确认请求的应用程序是否确实已结束的原因。如果指定的尚未结束,则此时立即结束(移除 -WhatIf 可以真正结束它们):

#requires -Version 3

$list = Get-Process |
    Where-Object -FilterScript {
        $_.MainWindowHandle -ne 0
    } |
    Select-Object -Property Name, Description, MainWindowTitle, Company, ID |
    Out-GridView -Title 'Choose Application to Kill' -PassThru

# try and close peacefully
$list.ID | ForEach-Object -Process {
    $process = Get-Process -Id $_
    $null = $process.CloseMainWindow()
}

# give user 15 seconds to respond
Start-Sleep -Seconds 15

# check to see if selected programs closed, and if not, kill
# them anyway (remove -WhatIf to actually kill them)
$list.ID |
ForEach-Object -Process {
    Get-Process -Id $_ -ErrorAction SilentlyContinue
    } |
    Where-Object -FilterScript {
        $_.hasExited -eq $false
    } |
    Stop-Process -WhatIf

PowerShell 技能连载 - 创建“结束进程”应用

只需要通过一个管道命令,PowerShell 就能够打开一个包含正在运行的程序列表。您可以选择列表中的一个或多个进程(按住 CTRL 键选择多余一个条目),然后 PowerShell 可以结束选中的程序。

Get-Process |
  Where-Object { $_.MainWindowHandle -ne 0 } |
  Select-Object -Property Name, Description, MainWindowTitle, Company, ID |
  Out-GridView -Title 'Choose Application to Kill' -PassThru |
  Stop-Process -WhatIf

请注意代码中使用了 -WhatIf 来模拟结束进程。如果您希望真的结束进程,那么移除 -WhatIf 代码。

结束程序会导致选中的应用程序立即停止。所有未保存的数据都会丢失。

PowerShell 技能连载 - 打开网页

要快速地在 Internet Explorer 中打开新的网页,您可以像这样定义一个名为 Show-WebPage 的新函数:

#requires -Version 2

function Show-WebPage
{
    param
    (
        [Parameter(Mandatory = $true, HelpMessage = 'URL to open')]
        $URL
    )

    Start-Process -FilePath iexplore.exe -ArgumentList $URL
}

当您运行这段脚本并执行 Show-WebPage 时,该命令将询问您要打开的 URL。您也可以通过 Show-WebPage 的参数提交 URL。

请注意该函数使用了 Start-Process 命令来启动您指定的浏览器。如果您将这行代码改为:

Start-Process -FilePath $URL

那么将会使用您的缺省浏览器来打开该 URL。

PowerShell 技能连载 - 将“列出所有变量”功能加入 PowerShell

在前一个技能中我们展示了一个可以显示 PowerShell ISE 中所有打开的脚本的所有变量名的脚本。

以下是一个改造,能够在 PowerShell ISE 的“附加工具”菜单中新增一个“List Variables”命令:

$code = {
  $psise.CurrentPowerShellTab.Files |
  ForEach-Object {
        $errors = $null
        [System.Management.Automation.PSParser]::Tokenize($_.Editor.Text, [ref]$errors) |
        Where-Object { $_.Type -eq 'Variable'} |
        Select-Object -Property Content |
        Add-Member -MemberType NoteProperty -Name Script -Value $_.DisplayName -PassThru
      } |
      Sort-Object -Property Content, Script -Unique |
      Out-GridView -Title 'Variables in use' -PassThru
    }

$psise.CurrentPowerShellTab.AddOnsMenu.Submenus.Add('List Variables', $code, 'ALT+V')

当您运行这段代码后,您可以按下 ALT+V 打开一个网格窗口显示所有打开的脚本中用到的变量。

PowerShell 技能连载 - 列出所有脚本中的所有变量

是否希望列出 PowerShell ISE 中打开的所有文件中的变量清单?

以下是一段能够创建这样清单的代码:

$psise.CurrentPowerShellTab.Files |
  ForEach-Object {
        $errors = $null
        [System.Management.Automation.PSParser]::Tokenize($_.Editor.Text, [ref]$errors) |
        Where-Object { $_.Type -eq 'Variable'} |
        Select-Object -Property Content |
        Add-Member -MemberType NoteProperty -Name Script -Value $_.DisplayName -PassThru
    } |
    Sort-Object -Property Content, Script -Unique

PowerShell 技能连载 - 快捷循环

适用于 PowerShell 4.0 及以上版本

PowerShell 中有一系列循环的语法。以下是一些 PowerShell 4.0 中循环执行代码的不太常见的方法。这个例子将播放一段频率不断提高的声音(请确保打开了扬声器):

(1..100).Foreach{[Console]::Beep($_ * 100, 300)}

在 PowerShell 4.0 及以上版本,数组拥有了 Where()ForEach() 方法。您可以像这样写一个过滤器:

@(Get-Service).Where({$_.Status -eq 'Running'})

PowerShell 的语法糖能让您省略这些语句中的大括号:

@(Get-Service).Where{$_.Status -eq 'Running'}

请注意该方法是针对数组的。相对于传统的管道方法,这种方法速度更快,但是内存消耗更大。

PowerShell 技能连载 - 分析(所有)事件日志源

您也许知道 Get-EventLog 命令。该命令能从一个指定的系统日志中读取所有条目:

Get-EventLog -LogName System

然而,Get-EventLog 一次只能查询一个事件日志源。所以如果您希望在所有事件日志源中查找所有错误信息,您需要创建一个循环,并且需要知道事件日志名字。

以下是一个单行的查询所有事件日志的简单技巧:

Get-EventLog -List |
  Select-Object -ExpandProperty Entries -ErrorAction SilentlyContinue |
  Where-Object { $_.EntryType -eq 'Error' }