PowerShell 技能连载 - 启用远程管理

支持 PowerShell 2 以上版本

许多早先基于 DCOM 的命令需要打开“远程管理防火墙例外”,才能访问远程系统。其中包含 Get-WmiObject 等 Cmdlet。

一个启用该功能的简单办法是在管理员权限下运行以下命令:

1
netsh firewall set service remoteadmin enable

虽然该命令已经准备淘汰,不过它仍然能用,而且是配置防火墙的最简单方法。

PowerShell 技能连载 - 查找自启动项

支持 PowerShell 3 以上版本

If you’d like to know which programs start automatically on your machine, WMI may help:

如果您想了解有多少个程序随着您的机器自动启动,WMI 也许能帮上忙:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
PS C:\> Get-CimInstance -ClassName Win32_StartupCommand | Select-Object -Property Name, Location, User, Command, Description

Name : OneDrive
Location : HKU\S-1-5-21-2012478179-265285931-690539891-1001\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
User : DESKTOP-7AAMJLF\tobwe
Command : "C:\Users\tobwe\AppData\Local\Microsoft\OneDrive\OneDrive.exe" /background
Description : OneDrive

Name : Bluetooth
Location : Common Startup
User : Public
Command : C:\PROGRA~1\WIDCOMM\BLUETO~1\BTTray.exe
Description : Bluetooth

Name : Snagit 12
Location : Common Startup
User : Public
Command : C:\PROGRA~2\TECHSM~1\SNAGIT~1\Snagit32.exe
Description : Snagit 12

Name : RTHDVCPL
Location : HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
User : Public
Command : "C:\Program Files\Realtek\Audio\HDA\RtkNGUI64.exe" -s
Description : RTHDVCPL

...

PowerShell 技能连载 - 替换 CSV 文件列名

支持 PowerShell 2 以上版本

当读取 CSV 数据的时候,可能会希望重命名 CSV 的列名,以下是一个简单的实现:只需要一行一行地读取文本,并跳过第一行(第一行包括 CSV 的列名)。然后,将表头替换成一个自定义的列名:

1
2
3
4
5
$header = ‘NewHeader1’, 'NewHeader2', 'NewHeader3'

Get-Content N:\somepathtofile\userlist.csv -Encoding Default |
Select-Object -Skip 1 |
ConvertFrom-CSV -UseCulture -Header $header

PowerShell 技能连载 - 通过管道输入数据

在前一个技能里我们演示了 Convert-Umlaut 如何转换一个字符串中的特殊字符。这在一个函数接受管道输入的时候更有用。让我们来看看增加这种特性所需要做的改变。

在不支持管道的情况下,该函数大概长这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#requires -Version 3

function Convert-Umlaut
{
param
(
[Parameter(Mandatory)]
$Text
)

$output = $Text.Replace('ö','oe').Replace('ä','ae').Replace('ü','ue').Replace('ß','ss').Replace('Ö','Oe').Replace('Ü','Ue').Replace('Ä','Ae')
$isCapitalLetter = $Text -ceq $Text.toUpper()
if ($isCapitalLetter)
{
$output = $output.toUpper()
}
$output
}

可以通过这种方式执行:

1
2
PS C:\> Convert-Umlaut -Text "Mößler, Christiansön"
Moessler, Christiansoen

然而,它不能像这样执行:

1
PS C:\> "Mößler, Christiansön" | Convert-Umlaut

要增加管道功能,需要做两件事:

  1. 参数需要标记为支持管道数据。
  2. 在迭代中对每个输入的元素进行处理的代码需要放置在 “process“ 代码块中。

以下是改变后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#requires -Version 3

function Convert-Umlaut
{
param
(
[Parameter(Mandatory, ValueFromPipeline)]
$Text
)

process
{
$output = $Text.Replace('ö','oe').Replace('ä','ae').Replace('ü','ue').Replace('ß','ss').Replace('Ö','Oe').Replace('Ü','Ue').Replace('Ä','Ae')
$isCapitalLetter = $Text -ceq $Text.toUpper()
if ($isCapitalLetter)
{
$output = $output.toUpper()
}
$output
}
}

现在,也可以通过管道传输数据了:

1
2
PS C:\> "Mößler, Christiansön" | Convert-Umlaut
Moessler, Christiansoen

PowerShell 技能连载 - 替换类似 “Umlauts” 的特殊字符

支持 PowerShell 2.0 以上版本

有些时候我们需要将一些字符替换,例如德语的 “Umlauts”,来适应用户名或邮箱地址。

以下是一个演示如何实现这个功能的小函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#requires -Version 3

function Convert-Umlaut
{
param
(
[Parameter(Mandatory)]
$Text
)

$output = $Text.Replace('ö','oe').Replace('ä','ae').Replace('ü','ue').Replace('ß','ss').Replace('Ö','Oe').Replace('Ü','Ue').Replace('Ä','Ae')
$isCapitalLetter = $Text -ceq $Text.toUpper()
if ($isCapitalLetter)
{
$output = $output.toUpper()
}
$output
}

要转换一个字符串,请这样使用:

1
2
PS C:\> Convert-Umlaut -Text "Mößler, Christiansön"
Moessler, Christiansoen

PowerShell 技能连载 - 友好地使用 Robocopy

支持 PowerShell 2.0 以上版本

Robocopy 是一个用于拷贝文件的工具,它在 PowerShell 里的功能也是一样。然而您可以用 PowerShell 将 robocopy 封装在一个对用户友好的 PowerShell 函数中。通过这种方式,您不再需要记忆 robocopy 别扭的命令行选项。取而代之的是 PowerShell 参数和智能提示功能。

一次 robocopy 的调用可能看起来如下:

1
PS C:\> Invoke-Robocopy -Source $env:windir -Destination c:\logs -Filter *.log -Recurse -Open

以下是封装函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#requires -Version 3

function Invoke-Robocopy
{
param
(
[String]
[Parameter(Mandatory)]
$Source,

[String]
[Parameter(Mandatory)]
$Destination,

[String]
$Filter = '*',

[Switch]
$Recurse,

[Switch]
$Open
)

if ($Recurse)
{
$DoRecurse = '/S'
}
else
{
$DoRecurse = ''
}

robocopy $Source $Destination $Filter $DoRecurse /R:0

if ($Open)
{
explorer.exe $Destination
}
}

PowerShell 技能连载 - 直接使用 .NET 类型

Cmdlet 内含了纯 .NET 代码,所以感谢 cmdlet,我们通常无需接触 .NET 代码。不过,如果您需要的话仍然可以使用。以下是一系列调用示例,演示了如何调用 .NET 方法:

#requires -Version 2
[System.Convert]::ToString(687687687, 2)

[Math]::Round(4.6)

[Guid]::NewGuid()

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

[System.Net.DNS]::GetHostByName('dell1')
[System.Net.DNS]::GetHostByAddress('192.168.1.124')

[Environment]::SetEnvironmentVariable()

# dangerous, save your work first
[Environment]::FailFast('Oops')

Add-Type -AssemblyName PresentationFramework
$dialog = New-Object Microsoft.Win32.OpenFileDialog
$dialog.ShowDialog()
$dialog.FileName

PowerShell 技能连载 - 使用工作流来并发执行代码

如果您希望同时执行多个任务,以下有多种方法用 Powershell 实现。一种是使用工作流。它们是 PowerShell 3.0 中引入的:

#requires -Version 3
workflow Test-ParallelForeach
{
  param
  (
    [String[]]
    $ComputerName
  )

  foreach -parallel -throttlelimit 8 ($Machine in $ComputerName)
  {
    "Begin $Machine"
    Start-Sleep -Seconds (Get-Random -min 3 -max 5)
    "End $Machine"
  }
}

$list = 1..20

Test-ParallelForeach -ComputerName $list | Out-GridView

Test-ParallelForeach 处理一个计算机列表(在这个例子中,是一个数字列表)。它们同时执行。要控制资源的使用,并行循环将节流限制为 8,所以所以在这个例子中的 20 台计算机是 8 个一组处理的。

请注意使用工作流需要更多地了解它们的架构和限制。这个例子关注于工作流提供的并行循环技术。

PowerShell 技能连载 - 用 #requires 语句装饰脚本

PowerShell 支持一系列 #requires 语句。技术上它们是注释,但是 PowerShell 会检查这些语句所申明的必要条件,并且如果条件不满足,它将不会执行这个脚本。另外,#requires 语句能快速地告知您运行脚本的前提条件。

#requires -Modules PrintManagement
#requires -Version 3
#Requires -RunAsAdministrator

#requires 语句必须是一个脚本的第一条语句,并且它只对保存的脚本有效。

PowerShell 技能连载 - 不要混合不同的对象

如果您连续输出完全不同的对象,您可能丢失信息。请看这个例子:

#requires -Version 2

$hash = @{
Name = 'PowerShell Conference EU'
Date = 'April 20, 2016'
City = 'Hannover'
URL = 'www.psconf.eu'
}
New-Object -TypeName PSObject -Property $hash

$b = Get-Process -Id $pid
$b

当您运行这段代码时,您将得到这样的结果:

Date           URL           Name                     City
----           ---           ----                     ----
April 20, 2016 www.psconf.eu PowerShell Conference EU Hannover
                             powershell_ise

看起来 $b (process) 的几乎所有属性都丢失了。原因是 PowerShell 是实时输出对象的,而且首次提交的对象决定了在表格中显示哪些属性。所有接下来的对象都将纳入这张表格中。

如果您必须要输出不同的对象,请将它们用管道输出到 Out-Host。每次您输出到 Out-Host,PowerShell 都将创建一个具有新的表格标题的输出。