PowerShell 技能连载 - 查找 AD 复制失败信息

适用于 Windows 8.1、Server 2012 R2

在 Windows 8.1 和 Server 2012 R2 中,查看 Active Directory 复制失败的信息变得更简单了。Get-ADReplicationFailure 这个新的 cmdlet 将会输出最近的复制失败信息。用它来检查一个特定的域控制器的方法:

PS> Get-ADReplicationFailure dc1.test.com

或者检查整个站点:

PS> Get-ADReplicationFailure -Scope Site -Target Hannover

该 cmdlet 是随 Windows 8.1 和 Server 2012 R2 发布的 ActiveDirectory 模块的一部分。在您使用它之前,请确保您在控制面板/软件/启用或关闭 Windows 功能中启用了它。它是“远程服务器管理工具 (RSAT)”的一部分。如果您的 Windows 8.1 没有预装 RSAT,您可以从这里下载它:http://www.microsoft.com/de-de/download/details.aspx?id=39296。

要查看该模块提供的其它 cmdlet,请试试这行代码:

PS> Get-Command -Module ActiveDirectory

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Cmdlet          Add-ADCentralAccessPolicyMember                    ActiveDir...
Cmdlet          Add-ADComputerServiceAccount                       ActiveDir...
Cmdlet          Add-ADDomainControllerPasswordReplicationPolicy    ActiveDir...
Cmdlet          Add-ADFineGrainedPasswordPolicySubject             ActiveDir...
(...)

请注意针对 Windows 8.1/Server 2012 R2 的 RSAT提供了一些额外的 cmdlet,这些 cmdlet 在针对 Windows 早期版本的 RSAT 中并没有提供。

PowerShell 技能连载 - 有用的静态 .NET 方法

适用于 PowerShell 所有版本

PowerShell 可以调用 .NET 类型的静态方法。以下是一些很好用的单行代码:

[Math]::Round(7.9)

[Convert]::ToString(576255753217, 8)

[Guid]::NewGuid()

[Net.Dns]::GetHostByName('schulung12')

[IO.Path]::GetExtension('c:\test.txt')

[IO.Path]::ChangeExtension('c:\test.txt', 'bak')

要查看更多的用法,请删除类型(方括号中的文字)后的代码,然后键入两个冒号。PowerShell ISE 将会自动弹出一个快捷菜单列出该类型可用的方法。在 PowerShell 控制台中,只需要按下 TAB 键即可得到自动完成的建议。

您也可以将一个类型通过管道输出到 Get-Member 命令:

PS> [Math] | Get-Member -MemberType *Method -Static


   TypeName: System.Math

Name            MemberType Definition
----            ---------- ----------
Abs             Method     static sbyte Abs(sbyte value), static int16 Abs(int16 value), static int Abs(int value), sta...
Acos            Method     static double Acos(double d)
Asin            Method     static double Asin(double d)
Atan            Method     static double Atan(double d)
Atan2           Method     static double Atan2(double y, double x)
BigMul          Method     static long BigMul(int a, int b)
Ceiling         Method     static decimal Ceiling(decimal d), static double Ceiling(double a)
Cos             Method     static double Cos(double d)
Cosh            Method     static double Cosh(double value
DivRem          Method     static int DivRem(int a, int b, [ref] int result), static long DivRem(long a, long b, [ref] ...
Equals          Method     static bool Equals(System.Object objA, System.Object objB)
Exp             Method     static double Exp(double d)
Floor           Method     static decimal Floor(decimal d), static double Floor(double d)
IEEERemainder   Method     static double IEEERemainder(double x, double y)
Log             Method     static double Log(double d), static double Log(double a, double newBase)
Log10           Method     static double Log10(double d)
Max             Method     static sbyte Max(sbyte val1, sbyte val2), static byte Max(byte val1, byte val2), static int1...
Min             Method     static sbyte Min(sbyte val1, sbyte val2), static byte Min(byte val1, byte val2), static int1...
Pow             Method     static double Pow(double x, double y)
ReferenceEquals Method     static bool ReferenceEquals(System.Object objA, System.Object objB)
Round           Method     static double Round(double a), static double Round(double value, int digits), static double ...
Sign            Method     static int Sign(sbyte value), static int Sign(int16 value), static int Sign(int value), stat...
Sin             Method     static double Sin(double a)
Sinh            Method     static double Sinh(double value)
Sqrt            Method     static double Sqrt(double d)
Tan             Method     static double Tan(double a)
Tanh            Method     static double Tanh(double value)
Truncate        Method     static decimal Truncate(decimal d), static double Truncate(double d)

要查看某个方法的所有重载的签名,请去掉圆括号:

PS> Get-Something -Path test
You entered test.

PS> [Math]::Round

OverloadDefinitions
-------------------
static double Round(double a
static double Round(double value, int digits)
static double Round(double value, System.MidpointRounding mode)
static double Round(double value, int digits, System.MidpointRounding mode
static decimal Round(decimal d
static decimal Round(decimal d, int decimals)
static decimal Round(decimal d, System.MidpointRounding mode)
static decimal Round(decimal d, int decimals, System.MidpointRounding mode)

如何用 PowerShell 撰写心灵鸡汤

关于励志段子

微信上传着一个励志段子,大意是:

如果26个英文字母 A B C D EF G H I J K L M N O P Q R S T U V W X Y Z 分别等于:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 1516 17 18 19 20 21 22 23 24 25 26。那么:

  • Knowledge (知识): K+N+O+W+L+E+D+G+E= 11+14+15+23+12+5+4+7+5=96%
  • Workhard (努力工作):W+O+R+K+H+A+R+D= 23+15+18+11+8+1+18+4 =98%
  • Luck(好运) L+U+C+K=12+21+3+11=47%
  • Love(爱情) L+O+V+E=12+15+22+5=54%
  • Money(金钱) M+O+N+E+Y=13+15+14+5+25=72%
  • Leadership(领导能力)L+E+A+D+E+R+S+H+I+P=12+5+1+4+5+18+19+9+16=89%
  • ATTITUDE(心态)A+T+T+I+T+U+D+E=1+20+20+9+20+21+4+5=100%

于是得出结论:用什么样的态度去看待人生,就会得到什么样的人生。

分析

这样的心灵鸡汤是怎样来的呢?我们用 PowerShell 脚本来琢磨一下。

1
2
3
4
5
6
7
8
function Get-Weight([string]$word) {
$word = $word.ToLower()
#Write-Host ([System.Text.Encoding]::ASCII.GetBytes($word) |
# ForEach-Object { $_ - 96 })
return ([System.Text.Encoding]::ASCII.GetBytes($word) |
ForEach-Object { $_ - 96 } |
Measure-Object -Sum).Sum
}

这个函数可以对任意字符串求值,例如以下测试代码将返回 6(abc = 1+2+3):

1
Get-Weight 'abc'

现在可以测试一下段子里用到的几个单词,并对结果进行排序:

1
2
'Knowledge', 'Workhard', 'Luck', 'Love', 'Money', 'Leadership', 'ATTITUDE' |
Sort-Object -Property @{Expression = { Get-Weight $_ }}

结果符合预期:

Luck
Love
Money
Knowledge
Leadership
Workhard
ATTITUDE

如何撰写鸡汤

以上实现了输入任意字符串数组,对它们进行求值和排序。但是如何选出这些单词呢?我们可以找一篇长文,例如从麻省理工找到莎士比亚的《哈姆雷特》全文,将它输进去拆解成单词试试:

1
2
3
4
5
$resp = Invoke-WebRequest 'http://shakespeare.mit.edu/hamlet/full.html'
$fullText = $resp.ParsedHtml.documentElement.innerText
$words = [regex]::Matches($fullText, '\b\w+\b') |
ForEach-Object { $_.Value } |
Sort-Object -Unique

这样几行代码,就可以将《哈姆雷特》全文的所有单词挑出来进行排序,并将结果保存在 $words 变量中。

最后套用我们上面写好的函数即可实现对所有单词求值排序:

1
2
3
4
5
$words |
Sort-Object -Property @{Expression = { Get-Weight $_ }} |
ForEach-Object {
"$_`t$(Get-Weight $_)"
}

结果大概是这样:

word weight
a 1
c 3
d 4
e 5
bad 7
be 7
I 9
letters 99
firmament 99
temperance 100
Writing 100
prosperously 199
unproportioned 200

有了这个长长的表格之后,撰写鸡汤就容易多了。只要按顺序挑出一些单词,设计一下台词即可。

完整的代码如下:

function Get-Weight([string]$word) {
    $word = $word.ToLower()
    #Write-Host ([System.Text.Encoding]::ASCII.GetBytes($word) |
    #    ForEach-Object { $_ - 96 })
    return ([System.Text.Encoding]::ASCII.GetBytes($word) |
        ForEach-Object { $_ - 96 } |
        Measure-Object -Sum).Sum
}

# Test
# Get-Weight 'abc'

if (!$resp) {
    $resp = Invoke-WebRequest 'http://shakespeare.mit.edu/hamlet/full.html'
}

$fullText = $resp.ParsedHtml.documentElement.innerText
$words = [regex]::Matches($fullText, '\b\w+\b') |
    ForEach-Object { $_.Value } |
    Sort-Object -Unique

# The following code will procuce output:
# Luck
# Love
# Money
# Knowledge
# Leadership
# Workhard
# ATTITUDE
'Knowledge', 'Workhard', 'Luck', 'Love', 'Money', 'Leadership', 'ATTITUDE' |
    Sort-Object -Property @{Expression = { Get-Weight $_ }}

$words |
    Sort-Object -Property @{Expression = { Get-Weight $_ }} |
    ForEach-Object {
        "$_`t$(Get-Weight $_)"
    }

后记

完整的代码可以在这里下载。鸡汤的原文请参见《是哪位高人琢磨出的这条微信,太牛了》。顺便发现了原文中的一个计算 bug——Leadership(领导能力)应是L+E+A+D+E+R+S+H+I+P=12+5+1+4+5+18+19+8+9+16=97%,而不是 89%。

怎么样,有没有一点理工男秒杀心灵鸡汤的味道?

PowerShell 技能连载 - 为必须的参数弹出一个对话框

适用于 PowerShell 所有版本

通常,但您将一个参数定义为必选的,并且用户没有传入对应的实参,PowerShell 能够处理好这种情况并提示用户输入这个值:

function Get-Something
{
  param
  (
    [Parameter(Mandatory = $true)]
    $Path
  )

  "You entered $Path."
}

结果类似这样(您无法控制提示信息):

PS> Get-Something -Path test
You entered test.

PS> Get-Something
Cmdlet Get-Something at command pipeline position 1
Supply values for the following parameters:
Path: test
You entered test.

PS>

但是您是否知道还可以通过这种方式获取一个必选参数?

function Get-Something
{
  param
  (
    $Path = $(Read-Host 'Please, enter a Path value')
  )

  "You entered $Path."
}

这种方法将控制权交给您,以下是它看起来的样子:

PS> Get-Something -Path test
You entered test.

PS> Get-Something
Please, enter a Path value: test
You entered test.

PS>

PowerShell 技能连载 - 使用打开文件夹对话框

适用于 PowerShell 所有版本

为了在您的脚本中输入一些东西,以下是一个简单的打开“打开文件”对话框并让用户选择一个文件的函数。

function Show-OpenFileDialog
{
  param
  ($Title = 'Pick a File', $Filter = 'All|*.*|PowerShell|*.ps1')

  $type = 'Microsoft.Win32.OpenFileDialog'


  $dialog = New-Object -TypeName $type
  $dialog.Title = $Title
  $dialog.Filter = $Filter
  if ($dialog.ShowDialog() -eq $true)
  {
    $dialog.FileName
  }
  else
  {
    Write-Warning 'Cancelled'
  }
}

如您所见,您可以控制该对话框的标题栏和显示的文件类型。

PowerShell 技能连载 - 记录拒绝存取的文件夹

适用于 PowerShell 所有版本

当您用 Get-ChildItem 浏览文件系统的时候,您可能偶尔会碰到没有查看权限的文件夹。如果您希望将抛出次异常的所有文件夹都记录下来,请试试这个方法:

$result = Get-ChildItem -Path c:\Windows -Filter *.ps1 -Recurse -ErrorAction SilentlyContinue -ErrorVariable abcd

Write-Warning 'Unable to access these folders:'
Write-Warning ($abcd.TargetObject -join "`r`n")

这个技巧是隐藏所有错误提示(-ErrorAction SilentlyContinue)但将错误都保存到一个变量中(-ErrorVariable abce)。

PowerShell 技能连载 - 管理终端服务设置

_需要 ActiveDirectory 模块

有些时候您也许希望在一个 AD 账户中直接存取终端服务相关的属性。以下是一些演示如何实现该功能的示例代码:

$Identity = 'SomeUserName'

$distinguishedName = (Get-ADUser -Identity $Identity -Properties distinguishedName).distinguishedName
$ADUser = [ADSI]"LDAP://$distinguishedName"

$TSProfilePath = $ADUser.psbase.InvokeGet('terminalservicesprofilepath')
$TSHomeDir = $ADUser.psbase.InvokeGet('TerminalServicesHomeDirectory')
$TSHomeDrive = $ADUser.psbase.InvokeGet('TerminalServicesHomeDrive')
$TSAllowLogOn = $ADUser.psbase.InvokeGet('allowLogon')

PowerShell 技能连载 - 凭据混淆器

适用于 PowerShell ISE 3.0 及以上版本

虽然一般不建议将密码硬编码在脚本里,但有些情况下已经这么做了。相比于硬编码明文密码,一个最基本的改进是将密码混淆。密码混淆是一种弱的保护方式,但它能确保非掌握 PowerShell 知识的人员轻易地得到密码。

这是段小脚本会询问用户名和密码,然后生成一段混淆脚本来产生凭据对象。

当您运行下面这段脚本生成的脚本,在 $cred 变量将会保存一个包含用户名和密码的凭据对象,它可以用于任何带 -Credential 参数的 cmdlet。

$cred = Get-Credential -Message 'Enter Domain\Username and Password'
$pwd = $cred.Password
$user = $cred.UserName
$key = 1..32 | ForEach-Object { Get-Random -Maximum 256 }
$pwdencrypted = $pwd | ConvertFrom-SecureString -Key $key

$private:ofs = ' '

$generatedScript = @()
$generatedScript += '$password = ''{0}''' -f $pwdencrypted
$generatedScript += '$key = ''{0}''' -f "$key"

$generatedScript += '$passwordSecure = ConvertTo-SecureString -String $password -Key ([Byte[]]$key.Split('' ''))'
$generatedScript += '$cred = New-Object system.Management.Automation.PSCredential(''{0}'', $passwordSecure)' -f $user
$generatedScript += '$cred'

$file = $psise.CurrentPowerShellTab.Files.Add()
$file.Editor.Text = $generatedScript | Out-String
$file.Editor.SetCaretPosition(1,1)

自动生成的密码脚本看起来类似这样:

$password = '76492d1116743f0423413b16050a5345MgB8AHMAUQA3AFAAVwB0AGkAUQBUAC8AdwBqADYAUABVAFYAUwB4AEYAYgB4AFEAPQA9AHwAZgA0ADgAOQA4AGYANwA0AGEAMAA0ADUANwA5ADkAMwA5ADkAMwA1ADUANQA0AGYANwA5AGQANwBkAGYAOQBmAGEAYQA3ADMAYgBkADIAOQA3AGMAYQBmADUAMgA3ADEANwA3AGEAYgBmADAAYgA1AGYAYwAyADYAYgAzADkAOAA='
$key = '187 98 34 82 148 52 13 86 246 2 130 197 217 97 147 98 75 197 149 246 74 35 27 7 211 15 131 93 182 231 171 3'
$passwordSecure = ConvertTo-SecureString -String $password -Key ([Byte[]]$key.Split(' '))
$cred = New-Object system.Management.Automation.PSCredential('mickey\mouse', $passwordSecure)
$cred

PowerShell 技能连载 - 自动展开和内存消耗

适用于 PowerShell 3.0 及以上版本

在 PowerShell 3.0 中,增加了一个称为“自动回滚”的特性。通过这个特性,您可以这样书写代码:

(Get-ChildItem -Path $env:windir\system32 -Filter *.dll).VersionInfo

这行代码查找 System32 子文件夹下的所有 DLL 文件并且对它们进行迭代,对每个文件返回其 VersionInfo 属性(实际上是 DLL 版本)。在使用自动展开功能之前,您需要手工编写循环语句:

Get-ChildItem -Path $env:windir\system32 -Filter *.dll | ForEach-Object { $_.VersionInfo }

当您运行以上两段代码时,它们返回完全相同的结果。然而,您将立刻发现自动展开特性所带来的代价:它消耗了更多的时间才返回结果。第一行结果出来时可能要消耗 10 秒之多的时间,而“传统”的方法几乎是连续地返回信息。

总体消耗的时间是差不多的。实际上,自动展开特性等价的代码如下:

$data = Get-ChildItem -Path $env:windir\system32 -Filter *.dll
Foreach ($element in $data) { $element.VersionInfo }

自动展开代码更直观,更容易书写手写循环兼容性更好,更快输出结果。