PowerShell 技能连载 - 查找缺少邮箱地址的 Active Directory 用户

LDAP 查询的功能非常强大,可以帮助查找缺少信息的账户。

这段代码将返回所有带邮箱地址的 Active Directory 用户:

$searcher = [ADSISearcher]"(&(sAMAccountType=$(0x30000000))(mail=*))"
$searcher.FindAll() |
  ForEach-Object { $_.GetDirectoryEntry() } |
  Select-Object -Property sAMAccountName, name, mail

如果您想查询相反的内容,请通过“!”号进行相反的查询。以下代码可以返回所有缺少邮箱地址的 Active Directory 用户:

$searcher = [ADSISearcher]"(&(sAMAccountType=$(0x30000000))(!(mail=*)))"
$searcher.FindAll() |
  ForEach-Object { $_.GetDirectoryEntry() } |
  Select-Object -Property sAMAccountName, name, mail

PowerShell 技能月刊

编号 发布时间 标题 PDF
Vol.01 2013年06月 文件系统任务 下载
Vol.02 2013年07月 数组和哈希表 下载
Vol.03 2013年08月 日期、时间和文化 下载
Vol.04 2013年09月 对象和类型 下载
Vol.05 2013年10月 WMI 下载
Vol.06 2013年11月 正则表达式 下载
Vol.07 2013年12月 函数 下载
Vol.08 2013年12月 静态 .NET 方法 下载
Vol.09 2014年01月 注册表 下载
Vol.10 2014年02月 Internet 相关任务 下载
Vol.11 2014年03月 XML 相关任务 下载
Vol.12 2014年08月 安全相关任务 下载

如果您(和我一样)足够懒,也可以用这样一行 PowerShell 代码来下载:

1..12 | ForEach-Object { Invoke-WebRequest "http://powershell.com/cs/PowerTips_Monthly_Volume_$_.pdf" -OutFile "PowerTips_Monthly_Volume_$_.pdf" }

PowerShell 技能连载 - 自动找借口的脚本

译者注:您没有看错!这是近期最邪恶的一个技巧,文末有译者机器上的实验效果。

厌倦了每次自己想蹩脚的借口?以下脚本能让您每调用一次 Get-Excuse 就得到一个新的接口!您所需的一切只是 Internet 连接:

function Get-Excuse
{
  $url = 'http://pages.cs.wisc.edu/~ballard/bofh/bofhserver.pl'
  $ProgressPreference = 'SilentlyContinue'
  $page = Invoke-WebRequest -Uri $url -UseBasicParsing
  $pattern = '<br><font size = "\+2">(.+)'

  if ($page.Content -match $pattern)
  {
    $matches[1]
  }
}

如果您需要通过代理服务器或者身份认证来访问 Internet,那么请查看函数中 Invoke-WebRequest 的参数。您可以通过它提交代理服务器信息,例如身份验证信息。

译者注:以下是 Get-Excuse 为笔者找的“借口”,很有创意吧 ;-)

PS >Get-Excuse
your process is not ISO 9000 compliant
PS >Get-Excuse
evil hackers from Serbia.
PS >Get-Excuse
piezo-electric interference
PS >Get-Excuse
Bogon emissions
PS >Get-Excuse
because Bill Gates is a Jehovah's witness and so nothing can work on St. Swithin's day.
PS >Get-Excuse
Your cat tried to eat the mouse.
PS >Get-Excuse
It works the way the Wang did, what's the problem
PS >Get-Excuse
Telecommunications is upgrading.
PS >Get-Excuse
Your computer's union contract is set to expire at midnight.
PS >Get-Excuse
Daemon escaped from pentagram
PS >Get-Excuse
nesting roaches shorted out the ether cable
PS >Get-Excuse
We ran out of dial tone and we're and waiting for the phone company to deliver another bottle.
PS >Get-Excuse
Root nameservers are out of sync

PowerShell 技能连载 - 导出和导入 PowerShell 历史

PowerShell 保存了您键入的所有命令列表,但是当您关闭 PowerShell 时,这个列表就丢失了。

以下是一个保存当前命令历史到文件的单行代码:

Get-History | Export-Clixml $env:temp\myHistory.xml

当您启动一个新的 PowerShell 控制台或 ISE 编辑器实例时,您可以将保存的历史读入 PowerShell:

Import-Clixml $env:\temp\myHistory.xml | Add-History

不过,加载历史并不会影响键盘缓冲区,所以按下上下键并不会显示新导入的历史条目。然而,您可以用 TAB 自动完成功能来查找您之前输入的命令:

#(KEYWORD) <-现在按下(TAB)键!

PowerShell 技能连载 - 将单词首字母转换为大写

要正确地将单词首字母转换为大写,您可以用正则表达式或者一点系统函数:

用正则表达式的话,您可以这样做:

$sentence = 'here is some text where i would like the first letter to be capitalized.'
$pattern = '\b(\w)'
[RegEx]::Replace($sentence, $pattern, { param($x) $x.Value.ToUpper() })

用系统函数的话,这样做可以达到相同的效果:

$sentence = 'here is some text where i would like the first letter to be capitalized.'
(Get-Culture).TextInfo.ToTitleCase($sentence)

正则表达式稍微复杂一点,但是功能更多。例如如果出于某种古怪的原因,您需要将每个单词的首字母替换为它的 ASCII 码,那么正则表达式可以轻松地实现:

$sentence = 'here is some text where i would like the first letter to be capitalized.'
$pattern = '\b(\w)'
[RegEx]::Replace($sentence, $pattern, { param($x) [Byte][Char]$x.Value })

PowerShell 技能连载 - 查找缺省的 Outlook 配置文件

PowerShell 可以操作 COM 对象,例如 Outlook 应用程序。以下简单的两行代码能返回当前的 Outlook 配置文件名:

$outlookApplication = New-Object -ComObject Outlook.Application
$outlookApplication.Application.DefaultProfileName

PowerShell 技能连载 - PowerShell 4.0 中的动态方法

从 PowerShell 4.0 开始,方法名可以是一个变量。以下是一个简单的例子:

$method = 'ToUpper'
'Hello'.$method()

当您需要调用的方法须通过一段脚本计算得到的时候,这个特性十分有用。

function Convert-Text
{
  param
  (
    [Parameter(Mandatory)]
    $Text,
    [Switch]$ToUpper
  )

  if ($ToUpper)
  {
    $method = 'ToUpper'
  }
  else
  {
    $method = 'ToLower'
  }
  $text.$method()
}

以下是用户调用该函数的方法:

PS> Convert-Text 'Hello'
hello
PS> Convert-Text 'Hello' -ToUpper
HELLO

缺省情况下,该函数将文本转换为小写。当指定了开关参数 -ToUpper 时,函数将文本转换为大写。由于动态方法特性的支持,该函数不需要为此写两遍代码。

译者注:在旧版本的 PowerShell 中,您可以通过 .NET 方法(而不是脚本方法)中的反射来实现相同的目的。虽然它不那么整洁,但它能运行在 PowerShell 4.0 以下的环境:

function Convert-Text
{
  param
  (
    [Parameter(Mandatory)]
    $Text,
    [Switch]$ToUpper
  )

  if ($ToUpper)
  {
    $method = 'ToUpper'
  }
  else
  {
    $method = 'ToLower'
  }
  $methodInfo = $Text.GetType().GetMethod($method, [type[]]@())
  $methodInfo.Invoke($Text, $null)
}

PowerShell 技能连载 - PowerShell 4.0 中的动态参数

在 PowerShell 中,您可以使用变量来指代属性名。这段示例脚本定义了四个 profile 的属性名,然后在一个循环中分别查询这些属性值:

$list = 'AllUsersAllHosts','AllUsersCurrentHost','CurrentUserAllHosts','CurrentUserCurrentHost'
foreach ($property in $list)
{
    $profile.$property
}

您也可以在一个管道中使用它:

'AllUsersAllHosts','AllUsersCurrentHost','CurrentUserAllHosts','CurrentUserCurrentHost' |
ForEach-Object { $profile.$_ }

通过这种方式,您可以检查和返回 PowerShell 当前使用的所有 profile:

'AllUsersAllHosts','AllUsersCurrentHost','CurrentUserAllHosts','CurrentUserCurrentHost' |
ForEach-Object { $profile.$_ } |
Where-Object { Test-Path $_ }

类似地,您可以首先使用 Get-Member 来获取一个指定对象包含的所有属性。以下代码可以返回 PowerShell 的“PrivateData”对象中所有名字包含“color”的属性:

$host.PrivateData | Get-Member -Name *color* | Select-Object -ExpandProperty Name

接下来,您可以用一行代码获取所有的颜色设置:

$object = $host.PrivateData
$object | Get-Member -Name *color* -MemberType *property | ForEach-Object {
    $PropertyName = $_.Name
    $PropertyValue = $object.$PropertyName
    "$PropertyName = $PropertyValue"
} |
Out-GridView

PowerShell 技能连载 - 替换文本中的指定字符

如果您只是需要替换文本中字符出现的所有位置,这是很简单的。以下可以将文本中所有的“l”变为大写:

"Hello World".Replace('l', 'L')

然而有些时候,您需要替换特定位置的某几个字符。我们假设您的文本是一个比特掩码,并且您需要对某些比特置位或清除。以上代码是不能用的,因为它一口气改变了所有的位:

PS> "110100011110110".Replace('1', '0')

000000000000000

而且您也不能通过索引来改变字符。您可以读取一个字符(例如检查某一个位是否为“1”),但您无法改变它的值:

PS> "110100011110110"[-1] -eq '1'
False

PS> "110100011110110"[-2] -eq '1'
True

PS> "110100011110110"[-2] = '0'
无法对 System.String 类型的对象进行索引。

要改变一个字符串中的某些字符,请将它转换为一个 StringBuilder

PS> $sb = New-Object System.Text.StringBuilder("1101100011110110")

PS> $sb[-1]
0

PS> $sb[-1] -eq '1'
False

PS> $sb[-2] -eq '1'
True

PS> $sb[-2] = '0'

PS> $sb[-2] -eq '1'
False

PS> $sb.ToString()
110100011110100

以下是将二进制转换为十进制格式的方法:

PS> $sb.ToString()
110100011110100

PS> [System.Convert]::ToInt64($sb.ToString(), 2)
26868

PowerShell 技能连载 - 怪异的文本格式化(以及解决方法)

试试以下的代码并且找到问题所在:

$desc = Get-Process -Id $pid | Select-Object -Property Description
"PowerShell process description: $desc"

这段代码的目的是获取 PowerShell 宿主进程并且读取进程的描述信息,然后输出到字符串。它的结果看起来是怪异的:

PowerShell process description: @{Description=Windows PowerShell}

这是因为代码中选择了整个 Description 属性,而且结果不仅是描述字符串,而且包括了整个属性:

PS> $desc

Description
-----------
Windows PowerShell ISE

当您只选择一个属性时,请确保使用 -ExpandProperty 而不是 -Property。前者避免产生一个属性列,并且字符串看起来正常了:

PS> $desc = Get-Process -Id $pid | Select-Object -ExpandProperty Description
PS> "PowerShell process description: $desc"
PowerShell process description: Windows PowerShell ISE