PowerShell 技能连载 - 字符串操作进阶

适用于 PowerShell 5.1 及以上版本

字符串处理是脚本语言最核心的能力之一。虽然 PowerShell 的字符串基础操作大家都会,但很多高效技巧并不为人熟知——比如 -f 格式化操作符、多行 Here-String 的高级用法、StringBuilder 的大批量拼接、编码转换、Base64 处理等。掌握这些进阶技巧,可以大幅简化文本处理任务。

本文将讲解 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
# -f 格式化操作符
$name = "World"
$date = Get-Date
$num = 12345.6789

Write-Host ("Hello, {0}! Today is {1:yyyy-MM-dd}." -f $name, $date)
Write-Host ("数字:{0:N2}" -f $num) # 12,345.68
Write-Host ("百分比:{0:P1}" -f 0.856) # 85.6 %
Write-Host ("十六进制:{0:X4}" -f 255) # 00FF
Write-Host ("左对齐:{0,-20} | {1}" -f "Name", "Value")
Write-Host ("右对齐:{0,20} | {1}" -f "Name", "Value")

# 格式化表格输出
$data = @(
@{ Name = "CPU"; Value = 75.2; Unit = "%" },
@{ Name = "Memory"; Value = 82.1; Unit = "%" },
@{ Name = "Disk"; Value = 45.6; Unit = "%" }
)

Write-Host ("{0,-15} {1,10} {2,5}" -f "Metric", "Value", "Unit")
Write-Host ("{0,-15} {1,10} {2,5}" -f "-------", "-----", "----")
foreach ($item in $data) {
Write-Host ("{0,-15} {1,10:N1} {2,5}" -f $item.Name, $item.Value, $item.Unit)
}

# 字符串插值(PowerShell 7+)
$version = "7.4"
Write-Host "PowerShell $version 运行在 $($env:OS) 上"

执行结果示例:

1
2
3
4
5
6
7
8
9
10
11
Hello, World! Today is 2025-07-04.
数字:12,345.68
百分比:85.6 %
十六进制:00FF
左对齐:Name | Value
右对齐: Name | Value
Metric Value Unit
------- ----- ----
CPU 75.2 %
Memory 82.1 %
Disk 45.6 %

Here-String 高级用法

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
# 展开 Here-String(双引号)——变量会被替换
$appName = "MyApp"
$version = "2.5.0"
$env = "production"

$deployScript = @"
#!/usr/bin/env bash
echo "Deploying $appName v$version to $env"
docker pull registry.example.com/${appName}:${version}
docker stop $appName || true
docker rm $appName || true
docker run -d --name $appName -p 8080:80 registry.example.com/${appName}:${version}
echo "Deployment complete"
"@

Write-Host $deployScript

# 非展开 Here-String(单引号)——原样保留
$rawJson = @'
{
"name": "${APP_NAME}",
"version": "${VERSION}",
"port": ${PORT}
}
'@

Write-Host "模板内容:" -ForegroundColor Cyan
Write-Host $rawJson

# 用 Here-String 构建 SQL 查询
$sql = @"
SELECT
s.ServerName,
s.IPAddress,
d.DiskName,
d.TotalSizeGB,
d.UsedSizeGB,
ROUND(d.UsedSizeGB / d.TotalSizeGB * 100, 1) AS UsagePercent
FROM Servers s
JOIN Disks d ON s.ServerId = d.ServerId
WHERE d.UsedSizeGB / d.TotalSizeGB > 0.8
ORDER BY UsagePercent DESC
"@

Write-Host "SQL 查询已准备,长度:$($sql.Length) 字符" -ForegroundColor Green

执行结果示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env bash
echo "Deploying MyApp v2.5.0 to production"
docker pull registry.example.com/MyApp:2.5.0
docker stop MyApp || true
docker rm MyApp || true
docker run -d --name MyApp -p 8080:80 registry.example.com/MyApp:2.5.0
echo "Deployment complete"

模板内容:
{
"name": "${APP_NAME}",
"version": "${VERSION}",
"port": ${PORT}
}

SQL 查询已准备,长度:312 字符

高效字符串处理

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
# StringBuilder —— 大量字符串拼接
$sb = [System.Text.StringBuilder]::new()

$sw = [System.Diagnostics.Stopwatch]::StartNew()

# 生成 CSV 报告
$sb.AppendLine("Server,Status,CPU,Memory,Disk") | Out-Null
1..1000 | ForEach-Object {
$server = "SRV$($_.ToString('D3'))"
$cpu = Get-Random -Min 10 -Max 95
$mem = Get-Random -Min 20 -Max 90
$disk = Get-Random -Min 15 -Max 85
$status = if ($cpu -gt 80 -or $mem -gt 80) { "Warning" } else { "OK" }
$null = $sb.AppendLine("$server,$status,$cpu,$mem,$disk")
}

$sw.Stop()
Write-Host "生成 1000 行 CSV 耗时:$($sw.ElapsedMilliseconds)ms" -ForegroundColor Green

# 保存到文件
$sb.ToString() | Set-Content "C:\Reports\servers.csv" -Encoding UTF8

# 字符串分割与连接
$csvLine = "SRV001,OK,45,62,30"
$fields = $csvLine -split ','
Write-Host "服务器:$($fields[0]),状态:$($fields[1])"

$joined = $fields[0..2] -join ' | '
Write-Host "部分字段:$joined"

# 多行文本处理
$logContent = @"
[INFO] Service started
[WARN] High memory usage detected
[ERROR] Database connection timeout
[INFO] Retrying connection
[ERROR] Failed after 3 retries
"@

# 按行处理
$lines = $logContent -split "`n" | Where-Object { $_.Trim() }
$errors = $lines | Where-Object { $_ -match '\[ERROR\]' }
$warnings = $lines | Where-Object { $_ -match '\[WARN' }

Write-Host "错误行:$($errors.Count) 条"
Write-Host "警告行:$($warnings.Count) 条"
$errors | ForEach-Object { Write-Host " $_" -ForegroundColor Red }

执行结果示例:

1
2
3
4
5
6
7
8
生成 1000 行 CSV 耗时:12ms

服务器:SRV001,状态:OK
部分字段:SRV001 | OK | 45
错误行:2 条
警告行:1 条
[ERROR] Database connection timeout
[ERROR] Failed after 3 retries

编码与 Base64

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
49
50
51
# 字符串转 Base64
function ConvertTo-Base64 {
param([Parameter(Mandatory)][string]$Text)

$bytes = [System.Text.Encoding]::UTF8.GetBytes($Text)
return [Convert]::ToBase64String($bytes)
}

# Base64 转字符串
function ConvertFrom-Base64 {
param([Parameter(Mandatory)][string]$Base64)

$bytes = [Convert]::FromBase64String($Base64)
return [System.Text.Encoding]::UTF8.GetString($bytes)
}

$original = "Hello, 世界!PowerShell 字符串处理"
$encoded = ConvertTo-Base64 -Text $original
$decoded = ConvertFrom-Base64 -Text $encoded

Write-Host "原文:$original"
Write-Host "Base64:$encoded"
Write-Host "解码:$decoded"

# 文件编码检测与转换
function Convert-FileEncoding {
param(
[Parameter(Mandatory)][string]$Path,
[ValidateSet("UTF8", "UTF8BOM", "ASCII", "Unicode")]
[string]$TargetEncoding = "UTF8"
)

$content = [System.IO.File]::ReadAllText($Path)
$encoding = switch ($TargetEncoding) {
"UTF8" { [System.Text.UTF8Encoding]::new($false) }
"UTF8BOM" { [System.Text.UTF8Encoding]::new($true) }
"ASCII" { [System.Text.Encoding]::ASCII }
"Unicode" { [System.Text.Encoding]::Unicode }
}

$newPath = [System.IO.Path]::ChangeExtension($Path, ".$TargetEncoding.txt")
[System.IO.File]::WriteAllText($newPath, $content, $encoding)
Write-Host "已转换:$Path => $newPath ($TargetEncoding)" -ForegroundColor Green
}

# URL 编码/解码
$url = "https://api.example.com/search?q=PowerShell 字符串&lang=zh-CN"
$encoded = [System.Uri]::EscapeDataString($url)
Write-Host "URL 编码:$($encoded.Substring(0, 50))..."
$decoded = [System.Uri]::UnescapeDataString($encoded)
Write-Host "URL 解码:$decoded"

执行结果示例:

1
2
3
4
5
原文:Hello, 世界!PowerShell 字符串处理
Base64:SGVsbG8sIOS4lueVjO+8jFBvd2VyU2hlbGwg5a2X56ym5ZyOPGJyPuaIkA==
解码:Hello, 世界!PowerShell 字符串处理
URL 编码:https%3A%2F%2Fapi.example.com%2Fsearch%3Fq%3DPowe...
URL 解码:https://api.example.com/search?q=PowerShell 字符串&lang=zh-CN

注意事项

  1. 不可变性:.NET 中字符串不可变,每次拼接都创建新对象。大量拼接使用 StringBuilder
  2. 编码陷阱:PowerShell 5.1 的 Set-Content 默认编码不是 UTF-8,显式指定 -Encoding UTF8
  3. Here-String 缩进@""@ 必须在行首,不能有前导空格(关闭标记)
  4. 性能比较-split/-join 比字符串的 .Split()/.Join() 方法更符合 PowerShell 风格,但底层性能一致
  5. 比较操作:字符串比较默认大小写不敏感,使用 -ceq/-clike 进行大小写敏感比较
  6. 空值处理$null.ToString() 会报错,字符串操作前检查 $null