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 技能连载 - 通过鼠标滚轮控制 PowerShell 的透明度

在 Windows 10 中,当按下 CTRL+SHIFT 并滚动鼠标滚轮,就可以方便地控制控制台窗口的透明度,包括 PowerShell 控制台窗口。

查看更多

评论

PowerShell 技能连载 - Retrieving Windows Product Key

以下是获取当前 Windows 产品序列号的单行代码:

1
(Get-WmiObject -Class "SoftwareLicensingService").OA3xOriginalProductKey

查看更多

评论

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

查看更多

评论

PowerShell 技能连载 - 在 Windows 10 中安装 Linux

Windows 中带有适用于 Linux 的 Windows 子系统 (WSL) 功能,您可以通过它来运行各种 Linux 发型版。通过提升权限的 PowerShell 来启用 WSL:

1
Enable-WindowsOptionalFeature -FeatureName Microsoft-Windows-Subsystem-Linux -Online

下一步,在 Windows 10 中打开 Microsoft Store,并且搜索 “Linux”。安装某个支持的 Linux 发行版(例如,Debian)!

这是全部的步骤。您现在可以在 PowerShell 里或通过开始菜单打开 Debian。只需要运行 “Debian” 命令。当您首次运行时,您需要设置一个 root 账号和密码。

您也许已经知道,PowerShell 是跨平台的,并且可以运行在 Linux 上。如果您希望使用新的 Debian 环境并且测试在 Linux 上运行 PowerShell,请在 Debian 控制台中运行以下代码:

1
2
3
4
5
6
7
sudo apt-get update
sudo apt-get install curl apt-transport-https
curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
sudo sh -c 'echo "deb https://packages.microsoft.com/repos/microsoft-debian-stretch-prod stretch main" > /etc/apt/sources.list.d/microsoft.list'
sudo apt-get update
sudo apt-get install -y powershell
pwsh

下一步,从 Debian 中,运行 “pwsh“ 命令切换到 PowerShell 6。

查看更多

评论

PowerShell 技能连载 - 使用本地化的用户名和组名

以下是一行返回由当前用户 SID 解析成名字的代码:

1
([System.Security.Principal.WindowsIdentity]::GetCurrent()).User.Translate( [System.Security.Principal.NTAccount]).Value

您可能会反对说查询 $env:username 环境变量来完成这个目的更容易,的确是这样的。然而在许多场景将 SID 转换为名字很有用。例如,如果您必须知道某个常见账户或组的确切(本地)名字,例如本地的 Administrators 组,只需要使用语言中性的 SID 来翻译它:

1
2
3
PS> ([System.Security.Principal.SecurityIdentifier]'S-1-5-32-544').Translate( [System.Security.Principal.NTAccount]).Value

VORDEFINIERT\Administratoren

类似地,以下代码总是列出本地的 Administrators,无论是什么语言的操作系统:

1
2
3
4
5
$admins = ([System.Security.Principal.SecurityIdentifier]'S-1-5-32-544').Translate( [System.Security.Principal.NTAccount]).Value
$parts = $admins -split '\\'
$groupname = $parts[-1]

Get-LocalGroupMember -Group $groupname

查看更多

评论

PowerShell 技能连载 - 当前用户的 SID

以下是一行返回当前用户 SID 并且可以用于登录脚本的代码,例如:

1
([System.Security.Principal.WindowsIdentity]::GetCurrent()).User.Value

查看更多

评论

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
function Get-PortInfo
{
param
(
[Parameter(Mandatory)]
[Int]
$Port,

[Parameter(Mandatory)]
[Int]
$TimeoutMilliseconds,

[String]
$ComputerName = $env:COMPUTERNAME
)

# try and establish a connection to port async
$tcpobject = New-Object System.Net.Sockets.TcpClient
$connect = $tcpobject.BeginConnect($computername,$port,$null,$null)

# wait for the connection no longer than $timeoutMilliseconds
$wait = $connect.AsyncWaitHandle.WaitOne($timeoutMilliseconds,$false)

# return rich information
$result = @{
ComputerName = $ComputerName
}

if(!$wait) {
# timeout
$tcpobject.Close()
$result.Online = $false
$result.Error = 'Timeout'
} else {
try {
# try and complete the connection
$null = $tcpobject.EndConnect($connect)
$result.Online = $true
}
catch {
$result.Online = $false
}
$tcpobject.Close()
}
$tcpobject.Dispose()

[PSCustomObject]$result
}

扫描端口现在变得十分简单:

1
2
3
4
5
6
7
8
9
10
11
12
PS> Get-PortInfo -Port 139 -TimeoutMilliseconds 1000

ComputerName Online
------------ ------
DESKTOP-7AAMJLF True


PS> Get-PortInfo -Port 139 -TimeoutMilliseconds 1000 -ComputerName storage2

Error ComputerName Online
----- ------------ ------
Timeout storage2 False

查看更多

评论

PowerShell 技能连载 - 重制控制台颜色

在 PowerShell 控制台里很容易把控制台颜色搞乱。一个不经意的调用意外地改变了颜色值,或者脚本错误地设置了颜色,可能会导致意外的结果。要验证这种情况,请打开一个 PowerShell 控制台(不是编辑器!),并且运行这段代码:

1
PS> [Console]::BackgroundColor = "Green"

要快速地清除颜色,请运行这段代码:

1
PS> [Console]::ResetColor()

接着运行 Clear-Host 可以清空显示。

查看更多

评论