PowerShell 技能连载 - 别名有可能带来风险

适用于所有 PowerShell 版本

在 PowerShell 中执行命令时,别名享有最高的优先权,所以如果遇到了有歧义的命令,PowerShell 将会优先执行别名命令。

这样可能很危险:如果您允许别人更改您的 PowerShell 环境,并且私下添加了您不知道的别名,那么您的脚本执行起来的效果可能完全不同。

Here is a simple call that adds the alias Get-ChildItem and lets it point to ping.exe:
以下是一个简单的例子,创建了一个名为 Get-ChildItem 的别名,并指向 ping.exe

Set-Alias -Name Get-ChildItem -Value ping

这将导致一切都改变了:Get-ChildItem 不再列出文件夹内容了,而是变为 ping 的行为。甚至,所有的别名(例如 dirls)现在都指向 ping。我们假想一下如果别名指向了 format.exe,那么您的脚本会做什么?

PowerShell 技能连载 - 转换特殊字符(第二部分)

适用于所有 PowerShell 版本

在前一个技巧中我们演示了如何替换一段文本中的特殊字符。以下是另一种方法,虽然慢一点,但是更容易维护。它也演示了一个大小写敏感的哈希表:

function ConvertTo-PrettyText($Text)
{
  $hash = New-Object -TypeName HashTable

  $hash.'ä' = 'ae'
  $hash.'ö' = 'oe'
  $hash.'ü' = 'ue'
  $hash.'ß' = 'ss'
  $hash.'Ä' = 'Ae'
  $hash.'Ö' = 'Oe'
  $Hash.'Ü' = 'Ue'

  Foreach ($key in $hash.Keys)
  {
    $Text = $text.Replace($key, $hash.$key)
  }
  $Text
}

请注意该函数并不是以 @{} 的方式定义一个哈希表,而是构造了一个 HashTable 对象。由于 PowerShell 所带的哈希表是大小写不敏感的,而这个函数创建的哈希表是大小写敏感的。这一点非常重要,因为该函数期望对大小写字母作区分。

PS> ConvertTo-PrettyText -Text 'Mr. Össterßlim'
Mr. Oesstersslim

PS>

如果您想要指定 ASCII 码,以下是一个用 ASCII 码作为键的变体:

function ConvertTo-PrettyText($Text)
{
  $hash = @{
    228 = 'ae'
    246 = 'oe'
    252 = 'ue'
    223 = 'ss'
    196 = 'Ae'
    214 = 'Oe'
    220 = 'Ue'
  }

  foreach($key in $hash.Keys)
  {
    $Text = $text.Replace([String][Char]$key, $hash.$key)
  }
  $Text
}

PowerShell 技能连载 - 转换特殊字符(第一部分)

适用于所有 PowerShell 版本

有些时候我们需要将特殊字符转换为其它字符。以下是一个实现该功能的小函数:

function ConvertTo-PrettyText($Text)
{
  $Text.Replace('ü','ue').Replace('ö','oe').Replace('ä', 'ae' ).Replace('Ü','Ue').Replace('Ö','Oe').Replace('Ä', 'Ae').Replace('ß', 'ss')
}

只要根据需要添加 Replace() 调用来处理文本即可。请注意 Replace() 是大小写敏感的,这样比较好:您可以针对大小写来做替换。

PS> ConvertTo-PrettyText -Text 'Mr. Össterßlim'
Mr. Oesstersslim

PowerShell 技能连载 - 测试不带别名的脚本

适用于所有 PowerShell 版本

别名在交互式 PowerShell 控制台中用起来很酷,但是不应在脚本中使用它们。在脚本中,请使用基础的命令(所以请使用 Get-ChidItem 而不是 dirls)。

要测试一个脚本,您可以删除所有的别名然后试试脚本是否任然能运行。以下是如何删除特定 PowerShell 会话中的所有别名的方法(它不会影响其它 PowerShell 会话,并且不会永久地删除内置的别名):

PS> Get-Alias | ForEach-Object { Remove-Item -Path ("Alias:\" + $_.Name) -Force }

PS> dir
dir : The term 'dir' is not recognized as the name of a cmdlet, function, script file, or operable
program. Check the spelling of the name, or if a path was included, verify that the path is correct
and try again.
At line:1 char:1
+ dir
+ ~~~
    + CategoryInfo           : ObjectNotFound: (dir:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

PS> Get-Alias

如您所见,所有别名都清空了。现在如果一个脚本使用了别名,它将会抛出一个异常。而关闭并重启 PowerShell 之后,所有内置的别名都恢复了。

PowerShell 技能连载 - 删除别名

适用于所有 PowerShell 版本

虽然您可以轻松地用 New-AliasSet-Alias 来创建新的别名,但是没有一个 cmdlet 可以删除别名。

PS> Set-Alias -Name devicemanager -Value devmgmt.msc

PS> devicemanager

PS>

要删除一个别名,您通常需要重启动您的 PowerShell。或者您可以通过 Alias: 虚拟驱动器删除它们:

PS> del Alias:\devicemanager

PS>

PowerShell 技能连载 - 快速查找 AD 账户

适用于所有 PowerShell 版本

您不必使用额外的 cmdlet 就能在您的活动目录中搜索用户账户或计算机。假设您已登录了一个域,只需要使用这段代码:

$ldap = '(&(objectClass=computer)(samAccountName=dc*))'
$searcher = [adsisearcher]$ldap

$searcher.FindAll()

这段代码将查找所有以“dc”开头的计算机账户。$ldap 可以是任何合法的 LDAP 查询语句。要查找用户,请将“computer”替换为“user”。

PowerShell 技能连载 - 从独立的文件中加载函数

适用于 PowerShell 3.0 及以上版本

为了让事情简化一些,您可能希望将 PowerShell 函数存放在一个独立的文件中。要将这些函数加载到您的业务脚本中,您可以使用这个简单的方法:

请确保包含 PowerShell 函数的脚本文件和业务脚本存放在同一个文件夹下。然后,在您的业务脚本中使用这行简单的代码:

. "$PSScriptRoot\library1.ps1"

这行代码将会从当前脚本存放的文件夹中加载一个称为“library1.ps1”的脚本。不要漏了前面的 . 和空格:“点加文件名”的方式执行一个文件,能够确保该文件中的所有变量和函数都在调用者的上下文中定义,并且当脚本执行完以后不会被清除掉。

请注意 $PSScriptRoot 总是指向脚本所在文件夹的路径(从 PowerShell 3.0 开始)。请确保已经保存了您的脚本,因为只有保存过的脚本才有父文件夹。

PowerShell 技能连载 - 创建优越的报告

当您克隆对象时,您可以修改它们的所有属性。克隆对象时可以导致原始对象“分离”,这是一个不错的主意。当您克隆了对象,您可以对该对象做任意的操作,例如修改或调整它的属性。

只需要用 Select-Object 命令就可以克隆对象。

这个例子列出文件夹中的内容,然后通过 Select-Object 处理,然后将其中的一些数据格式修饰一下。

Get-ChildItem -Path c:\windows |
  # clone the objects and keep the properties you want/add new properties (like "age...")
  Select-Object -Property LastWriteTime, 'Age(days)', Length, Name, PSIsContainer |
  # change the properties of the cloned object as you like
  ForEach-Object {
    # calculate the file/folder age in days
    $_.'Age(days)' = (New-Timespan -Start $_.LastWriteTime).Days

    # if it is a file, change size in bytes to size in MB
    if ($_.PSisContainer -eq $false)
    {
      $_.Length = ('{0:N1} MB' -f ($_.Length / 1MB))
    }

    # do not forget to return the adjusted object so the next one gets it
    $_
  } |
  # finally, select the properties you want in your report:
  Select-Object -Property LastWriteTime, 'Age(days)', Length, Name |
  # sort them as you like:
  Sort-Object -Property LastWriteTime -Descending |
  Out-GridView

该例子的结果以 MB 而不是字节为单位显示文件的大小,并且添加了一个称为“Age(days)”的列表示文件和文件夹创建以来的天数。

PowerShell 技能连载 - 接受多重输入

当您创建 PowerShell 函数时,以下是一个定义了既能够从参数中获取值,又能从管道中获取值的多功能 InputObject 参数的代码模板:

function Get-Something
{
  param
  (
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
    [Object[]]
    $InputObject
  )

  process
  {
    $InputObject | ForEach-Object {
      $element = $_
      "processing $element"
    }
  }
}

这是该函数的使用效果:

PS> Get-Something -InputObject 1,2,3,4
processing 1
processing 2
processing 3
processing 4

PS> 1,2,3,4 | Get-Something
processing 1
processing 2
processing 3
processing 4

请注意这个参数被定义成对象数组(所以它可以接收多个值)。然后,该参数值被送到 ForEach-Object 命令,将值一个一个取出来。这是针对第一个例子的调用方式。

要能够从管道中接收多个值,请确保对接收管道输入的参数设置了 ValueFromPipeline 属性。下一步,在函数中添加一段 Process 脚本块。这段代码充当循环的作用,和 ForEach-Object 十分相似,并且作用于管道送过来的每一个对象上。

PowerShell 技能连载 - 简单地读取注册表值

以下是最简单的读取注册表值的方法:

$Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
$Name = 'RegisteredOwner'

$result = (Get-ItemProperty -Path "Registry::$Key" -ErrorAction Stop).$Name

"Registered Windows Owner: $result"

只需要将 $Key 替换成注册表项,将 $Name 替换成注册表键,就能读取它的值。