PowerShell 技能连载 - 将 Windows 服务器转变为工作站

PowerShell 5 及以上版本提供了一个自动添加 Windows 功能的 cmdlet,所以如果您正在运行 Windows Server 并且想使用 Workstation 功能,请以管理员权限打开一个 PowerShell,然后运行以下代码:

1
Enable-WindowsOptionalFeature -FeatureName DesktopExperience -All -Online -NoRestart

PowerShell 技能连载 - 自动打印到 XPS 文件

XPS 是由 Microsoft 开发的类似 PDF 的文档格式。虽然它并没有大规模使用,但它仍然是一种打印信息到文件的很好的内部格式。要无人值守地打印到 XPS 文件,首先您需要设置一个新的打印机,该打印机将自动打印到一个指定的输出文件:

1
2
3
4
5
6
#requires -RunAsAdministrator

$OutPath = "$env:temp\out.xps"
$PrinterName = "XPSPrinter"
Add-PrinterPort -Name $OutPath
Add-Printer -Name $PrinterName -DriverName 'Microsoft XPS Document Writer v4' -PortName $OutPath

请确保 XPS 查看器已经安装:

1
2
#requires -RunAsAdministrator
Enable-WindowsOptionalFeature -Online -FeatureName Xps-Foundation-Xps-Viewer -NoRestart

基于以上的准备工作,现在要将输出结果自动打印到 XPS 文件非常简单。以下是一个日常使用的打印函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Out-PrinterXPS ($Path = $(Read-Host -Prompt 'XPS document path to create'))
{
$PrinterName = "XPSPrinter"
$OutPath = "$env:temp\out.xps"

$exists = Test-Path -Path $OutPath
if ($exists)
{
Remove-Item -Path $OutPath
}

$input | Out-Printer -Name $PrinterName
do
{
Start-Sleep -Milliseconds 500
$exists = Test-Path -Path $OutPath
} while (!$exists)

Move-Item -Path $OutPath -Destination $Path -Force
}

让我们试试使用它!以下是一行在桌面上创建系统清单报告的代码:

1
2
3
4
5
6
7
8
# print to this file
$Path = "$home\desktop\inventar.xps"

# pipe the data to the file
systeminfo.exe /FO CSV | ConvertFrom-Csv | Out-PrinterXPS -Path $Path

# open the XPS file with the built-in viewer
Invoke-Item -Path $Path

PowerShell 技能连载 - 从 Unicode 文件中移除 BOM

BOM(字节顺序标记)是在某些 Unicode 编码的文本文件特有的字节顺序。如果您收到一个包含了 BOM 的文件,而要处理它的其它系统并不支持 BOM,那么以下是如何用 PowerShell 移除这类文件中的 BOM 的方法:

1
2
3
4
5
6
function Remove-BomFromFile ($OldPath, $NewPath)
{
$Content = Get-Content $OldPath -Raw
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[IO.File]::WriteAllLines($NewPath, $Content, $Utf8NoBomEncoding)
}

现在要获取一个文件的 BOM 并将它转为一个无 BOM 的文件就很方便了:

1
2
3
$Path = "$env:temp\export.csv"
$NewPath = "$env:temp\export_new.csv"
Remove-BomFromFile -OldPath $Path -NewPath $NewPath

PowerShell 技能连载 - PowerShell 速查表汇编(第 2 部分)

在前一个技能中我们提供了一个很棒的 PowerShell 速查表。让我们来看看 PowerShell 能够怎样下载这些速查表:

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
# enable SSL download
$AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'
[System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols

# download page
$url = "https://github.com/PrateekKumarSingh/CheatSheets/tree/master/Powershell"
$page = Invoke-WebRequest -UseBasicParsing -Uri $url
$links = $page.Links |
Where-Object { $_.href -like '*.pdf' } |
Select-Object -Property title, href |
# turn URLs into directly downloadable absolute URLs
ForEach-Object {
$_.href = 'https://github.com/PrateekKumarSingh/CheatSheets/raw/master/Powershell/' + $_.title
$_
}

# create folder on your desktop
$Path = "$home\Desktop\CheatSheets"
$exists = Test-Path -Path $Path
if (!$exists) { $null = New-Item -Path $path -ItemType Directory }

# download cheat sheets
$links | ForEach-Object {
$docPath = Join-Path -Path $Path -ChildPath $_.Title
Start-BitsTransfer -Source $_.href -Destination $docPath -Description $_.title
# alternate way of downloading
# Invoke-WebRequest -UseBasicParsing -Uri $_.href -OutFile $docPath
}

# open folder
explorer $Path

当您运行这段脚本时,PowerShell 将下载所有的速查表并且将它们存放在桌面上一个名为 “CheatSheets” 的新文件夹中。祝您读得愉快!

PowerShell 技能连载 - 获取文本文件编码

文本文件可以以不同的编码存储。需要正确地指定编码,才能正确地读取它们。这是为什么多数读取文本文件的 cmdlet 提供 -Encoding 参数(例如 Get-Content)。如果没有指定正确的编码,您可能会看到一堆乱码。

那么如何(自动地)确认某个指定的文本文件所使用的编码?以下是一个好用的函数:

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
function Get-Encoding
{
param
(
[Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
[Alias('FullName')]
[string]
$Path
)

process
{
$bom = New-Object -TypeName System.Byte[](4)

$file = New-Object System.IO.FileStream($Path, 'Open', 'Read')

$null = $file.Read($bom,0,4)
$file.Close()
$file.Dispose()

$enc = [Text.Encoding]::ASCII
if ($bom[0] -eq 0x2b -and $bom[1] -eq 0x2f -and $bom[2] -eq 0x76)
{ $enc = [Text.Encoding]::UTF7 }
if ($bom[0] -eq 0xff -and $bom[1] -eq 0xfe)
{ $enc = [Text.Encoding]::Unicode }
if ($bom[0] -eq 0xfe -and $bom[1] -eq 0xff)
{ $enc = [Text.Encoding]::BigEndianUnicode }
if ($bom[0] -eq 0x00 -and $bom[1] -eq 0x00 -and $bom[2] -eq 0xfe -and $bom[3] -eq 0xff)
{ $enc = [Text.Encoding]::UTF32}
if ($bom[0] -eq 0xef -and $bom[1] -eq 0xbb -and $bom[2] -eq 0xbf)
{ $enc = [Text.Encoding]::UTF8}

[PSCustomObject]@{
Encoding = $enc
Path = $Path
}
}
}

以下是一段检查您用户配置文件中所有文本文件的测试代码:

1
2
3
4
5
6
7
8
9
10
PS> dir $home -Filter *.txt -Recurse | Get-Encoding

Encoding Path
-------- ----
System.Text.UnicodeEncoding C:\Users\tobwe\E006_psconfeu2019.txt
System.Text.UnicodeEncoding C:\Users\tobwe\E009_psconfeu2019.txt
System.Text.UnicodeEncoding C:\Users\tobwe\E027_psconfeu2019.txt
System.Text.ASCIIEncoding C:\Users\tobwe\.nuget\packages\Aspose.Words\18.12.0\...
System.Text.ASCIIEncoding C:\Users\tobwe\.vscode\extensions\ms-vscode.powers...
System.Text.UTF8Encoding C:\Users\tobwe\.vscode\extensions\ms-vscode.powers...

PowerShell 技能连载 - 打印 PDF 文件(第 2 部分)

在前一个技能中我们解释了如何用 PowerShell 将 PDF 文档发送到缺省的 PDF 打印机。这个通用方案对于简单场景是没问题的,但是无法让用户指定打印机。

如果使用特定的软件可以控制更多内容,因为您可以使用该软件暴露的特殊功能。

以下示例使用 Acrobat Reader 来打印到 PDF 文档。它展示了如何使用 Acrobat Reader 中的特殊选项来任意选择打印机,此外 Acrobat Reader 打印完文档将会自动关闭。

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
41
42
# PDF document to print out
$PDFPath = "C:\docs\document.pdf"

# find Acrobat Reader
$Paths = @(Resolve-Path -Path "C:\Program Files*\Adobe\*\reader\acrord32.exe")

# none found
if ($Paths.Count -eq 0)
{
Write-Warning "Acrobat Reader not installed. Cannot continue."
return
}

# more than one found, choose one manually
if ($Paths.Count -gt 1)
{
$Paths = @($Paths |
Out-GridView -Title 'Choose Acrobat Reader' -OutputMode Single)
}

$Software = $Paths[0]

# does it exist?
$exists = Test-Path -Path $Software
if (!$exists)
{
Write-Warning "Acrobat Reader not found. Cannot continue."
return
}

# choose printer
$printerName = Get-Printer |
Select-Object -ExpandProperty Name |
Sort-Object |
Out-GridView -Title "Choose Printer" -OutputMode Single

$printer = Get-Printer -Name $printerName
$drivername= $printer.DriverName
$portname=$printer.PortName
$arguments='/S /T "{0}" "{1}" "{2}" {3}' -f $PDFPath, $printername, $drivername, $portname

Start-Process $Software -ArgumentList $arguments -Wait

PowerShell 技能连载 - 打印 PDF 文件(第 1 部分)

如要自动打印 PDF 文档,不幸的是无法使用 Out-PrinterOut-Printer 只能发送纯文本文档到打印机。

不过,请看一下代码:

1
2
3
4
# adjust this path to the PDF document of choice
$Path = "c:\docs\document.pdf"

Start-Process -FilePath $Path -Verb Print

假设您已经安装了可以打印 PDF 文档的软件,这段代码将会把文档发送到关联的程序并自动将它打印到缺省的打印机。

一些软件(例如 Acrobat Reader)执行完上述操作之后仍会驻留在内存里。可以考虑这种方法解决:

1
2
3
4
5
6
7
8
9
# adjust this path to the PDF document of choice
$Path = "c:\docs\document.pdf"

# choose a delay (in seconds) for the print out to complete
$PrintDelay = 10

Start-Process -FilePath $Path -Verb Print -PassThru |
ForEach-Object{ Start-Sleep $printDelay; $_} |
Stop-Process