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”代表个人)。

PowerShell 技能连载 - 调用记事本打印文本

适用于 PowerShell 所有版本

若要调用记事本打印纯文本文件,请使用这行代码(请将路径替换成需要的值,否则将打印出长长的系统日志文件):

Start-Process -FilePath notepad -ArgumentList '/P C:\windows\WindowsUpdate.log'

PowerShell 技能连载 - 创建新的共享文件夹

适用于 PowerShell 所有版本

WMI 可以方便地创建新的共享文件夹。以下是一段创建本地共享文件夹的代码:

$ShareName = 'NewShare'
$Path = 'c:\123'

If (!(Get-WmiObject -Class Win32_Share -Filter "name='$ShareName'"))
{
  $Shares=[WMICLASS]"WIN32_Share"
  $Shares.Create($Path,$ShareName,0).ReturnValue
}
else
{
  Write-Warning "Share $ShareName exists already."
}

如果您有远程机器的管理员权限的话,也可以在远程的机器上创建共享文件夹。只需要像这样使用完整 WMI 即可:

$ShareName = 'NewShare'
$Path = 'c:\123'
$Server = 'MyServer'

If (!(Get-WmiObject -Class Win32_Share -Filter "name='$ShareName'"))
{
  $Shares=[WMICLASS]"\\$Server\root\cimv2:WIN32_Share"
  $Shares.Create($Path,$ShareName,0).ReturnValue
}
else
{
  Write-Warning "Share $ShareName exists already."
}

PowerShell 技能连载 - 改变 PowerShell 的优先级

适用于 PowerShell 所有版本

也许您有时候希望 PowerShell 脚本在后台运行,例如复制文件时,但又不希望脚本抢占过多 CPU 或干预其它任务。

一种减慢 PowerShell 脚本运行速度的方法是降低它们的优先级。以下是一个实现该效果的函数:

function Set-Priority
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [System.Diagnostics.ProcessPriorityClass]
        $Priority
    )

    $process = Get-Process -Id $pid
    $process.PriorityClass = $Priority
}

要降低脚本的优先级,请这样调用:

Set-Priority -Priority BelowNormal

您可以稍后将优先级调回 Normal,甚至可以调高优先级使脚本获得更多资源执行。例如需要执行更重的任务,不过这会使 UI 响应性变得更差。

PowerShell 技能连载 - 读取 PFX 证书

适用于 PowerShell 所有版本

当您使用 Get-PfxCertificate 命令时,您可以读取 PFX 证书文件,并使用证书来为脚本签名。然而,该命令总是交互式地询问证书的密码。

以下这段代码可以通过脚本来提交密码:

$PathToPfxFile = 'C:\temp\test.pfx'
$PFXPassword = 'test'

Add-Type -AssemblyName System.Security
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import($PathToPfxFile, $PFXPassword, 'Exportable')

$cert

PowerShell 技能连载 - 存取 SQLServer 数据库

适用于 PowerShell 所有版本及 SQLServer

您在使用 SQL Server 吗?以下是一段可以进行 SQL 查询并获取数据的 PowerShell 脚本模板。只需要确保填写了正确的用户信息、服务器地址和 SQL 语句即可:

$Database                       = 'Name_Of_SQLDatabase'
$Server                         = '192.168.100.200'
$UserName                         = 'DatabaseUserName'
$Password                       = 'SecretPassword'

$SqlQuery                       = 'Select * FROM TestTable'

# Accessing Data Base
$SqlConnection                  = New-Object -TypeName System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Data Source=$Server;Initial Catalog=$Database;user id=$UserName;pwd=$Password"
$SqlCmd                         = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText             = $SqlQuery
$SqlCmd.Connection              = $SqlConnection
$SqlAdapter                     = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand       = $SqlCmd
$set                            = New-Object data.dataset

# Filling Dataset
$SqlAdapter.Fill($set)

# Consuming Data
$Path = "$env:temp\report.hta"
$set.Tables[0] | ConvertTo-Html | Out-File -FilePath $Path

Invoke-Item -Path $Path

PowerShell 技能连载 - 创建彩色的 HTML 报告

适用于 PowerShell 所有版本

要将结果转换为彩色的自定义报告,只需要定义三个脚本块:一个生成 HTML 文档的头部,另一个生成尾部,还有一个对报表中的列表做循环,针对每一个列表项做处理。

然后,将这些脚本块传递给 ForEach-Object。它接受一个 begin、一个 process 和一个 end 脚本块。

以下是一个示例代码,演示了如何创建一个彩色的服务状态报表:

$path = "$env:temp\report.hta"

$beginning = {
 @'
    <html>
    <head>
    <title>Report</title>
    <STYLE type="text/css">
        h1 {font-family:SegoeUI, sans-serif; font-size:20}
        th {font-family:SegoeUI, sans-serif; font-size:15}
        td {font-family:Consolas, sans-serif; font-size:12}

    </STYLE>

    </head>
    <image src="http://www.yourcompany.com/yourlogo.gif" />
    <h1>System Report</h1>
    <table>
    <tr><th>Status</th><th>Name</th></tr>
'@
}

$process = {
    $status = $_.Status
    $name = $_.DisplayName

    if ($status -eq 'Running')
    {
        '<tr>'
        '<td bgcolor="#00FF00">{0}</td>' -f $status
        '<td bgcolor="#00FF00">{0}</td>' -f $name
        '</tr>'
    }
    else
    {
        '<tr>'
        '<td bgcolor="#FF0000">{0}</td>' -f $status
        '<td bgcolor="#FF0000">{0}</td>' -f $name
        '</tr>'
    }
}


$end = {
@'
    </table>
    </html>
    </body>
'@


}


Get-Service |
  ForEach-Object -Begin $beginning -Process $process -End $end |
  Out-File -FilePath $path -Encoding utf8

Invoke-Item -Path $path