PowerShell 技能连载 - 跨平台脚本开发

适用于 PowerShell 7.0 及以上版本(Windows / Linux / macOS)

PowerShell 7 是微软推出的跨平台版本,基于 .NET 构建,可以在 Windows、Linux 和 macOS 三个操作系统上运行。这为运维工程师带来了一个统一脚本语言的可能性——同一套 PowerShell 脚本理论上可以在不同平台上执行,减少了学习成本和维护负担。但现实中的跨平台开发远比”能跑起来”复杂得多。

不同操作系统在文件系统结构、路径规范、大小写敏感性、权限模型、包管理方式等方面存在显著差异。如果不做适配,一个在 Windows 上完美运行的脚本放到 Linux 上可能处处报错。比如路径分隔符的差异(\ vs /)、环境变量的读取方式、甚至某些 cmdlet 的可用性都会因平台而异。

本文将从平台检测与条件分支、路径与文件系统差异处理、跨平台工具函数库三个维度,介绍如何编写真正能在三大平台上稳定运行的 PowerShell 脚本。掌握这些技巧后,你可以将运维自动化脚本打包成跨平台模块,在混合环境中统一管理。

平台检测与条件分支

PowerShell 7 提供了三个内置布尔变量来标识当前运行平台:$IsWindows$IsLinux$IsMacOS。利用它们可以在脚本中实现条件分支,根据不同平台执行不同的逻辑。下面这个函数封装了平台检测逻辑,返回统一的结构化信息,方便在脚本的任何位置引用。

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-PlatformInfo {
[CmdletBinding()]
param()

$result = [ordered]@{
Platform = 'Unknown'
IsWindows = $false
IsLinux = $false
IsMacOS = $false
Architecture = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()
OSVersion = ''
ShellName = $PSVersionTable.ShellId
PSVersion = $PSVersionTable.PSVersion.ToString()
}

if ($IsWindows) {
$result.Platform = 'Windows'
$result.IsWindows = $true
$result.OSVersion = [System.Environment]::OSVersion.VersionString
}
elseif ($IsLinux) {
$result.Platform = 'Linux'
$result.IsLinux = $true
if (Test-Path /etc/os-release) {
$osRelease = Get-Content /etc/os-release -ErrorAction SilentlyContinue
$prettyName = $osRelease | Where-Object { $_ -match '^PRETTY_NAME=' }
if ($prettyName) {
$result.OSVersion = ($prettyName -split '=', 2)[1].Trim('"')
}
}
}
elseif ($IsMacOS) {
$result.Platform = 'macOS'
$result.IsMacOS = $true
$result.OSVersion = sw_vers -productVersion 2>$null
if ($result.OSVersion) {
$result.OSVersion = "macOS $($result.OSVersion)"
}
}

return [PSCustomObject]$result
}

# 使用示例:根据平台选择不同的包安装方式
$platform = Get-PlatformInfo
Write-Host "当前平台: $($platform.Platform) ($($platform.Architecture))"
Write-Host "操作系统: $($platform.OSVersion)"
Write-Host "PowerShell 版本: $($platform.PSVersion)"

在 Windows 上运行的输出大致如下:

1
2
3
当前平台: Windows (X64)
操作系统: Microsoft Windows 10.0.26100
PowerShell 版本: 7.5.0

在 Linux (Ubuntu) 上运行的输出大致如下:

1
2
3
当前平台: Linux (X64)
操作系统: Ubuntu 24.04.2 LTS
PowerShell 版本: 7.5.0

路径与文件系统差异处理

路径处理是跨平台脚本开发中最容易踩坑的地方。Windows 使用反斜杠 \ 作为路径分隔符,而 Linux 和 macOS 使用正斜杠 /。此外,Linux 的文件系统区分大小写,Windows 的 NTFS 默认不区分。下面这个函数库封装了常见的路径操作,确保在所有平台上行为一致。

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
function Join-PlatformPath {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Base,

[Parameter(Mandatory)]
[string]$Relative
)

# 统一使用 [System.IO.Path] 处理,自动适配当前平台
return [System.IO.Path]::Combine($Base, $Relative)
}

function Get-PlatformTempPath {
[CmdletBinding()]
param()

if ($IsWindows) {
return $env:TEMP
}
elseif ($IsMacOS -or $IsLinux) {
$tmpDir = $env:TMPDIR
if (-not $tmpDir) { $tmpDir = $env:XDG_RUNTIME_DIR }
if (-not $tmpDir) { $tmpDir = '/tmp' }
return $tmpDir
}
return '/tmp'
}

function Get-PlatformHomePath {
[CmdletBinding()]
param()

if ($IsWindows) {
return $env:USERPROFILE
}
return $env:HOME
}

function Resolve-PlatformCase {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Path
)

# 在区分大小写的文件系统上,验证路径的实际大小写
if ($IsLinux) {
if (-not (Test-Path $Path)) {
Write-Warning "路径不存在: $Path"
return $Path
}
$item = Get-Item $Path -ErrorAction SilentlyContinue
if ($item) {
return $item.FullName
}
}
return (Resolve-Path $Path -ErrorAction SilentlyContinue).Path
}

# 使用示例:构建跨平台的配置文件路径
$homePath = Get-PlatformHomePath
$configDir = Join-PlatformPath $homePath '.myapp'
$configFile = Join-PlatformPath $configDir 'config.json'
$tempDir = Get-PlatformTempPath

Write-Host "主目录: $homePath"
Write-Host "配置目录: $configDir"
Write-Host "配置文件: $configFile"
Write-Host "临时目录: $tempDir"

# 确保配置目录存在(跨平台方式)
if (-not (Test-Path $configDir)) {
New-Item -ItemType Directory -Path $configDir -Force | Out-Null
Write-Host "已创建配置目录: $configDir"
}

在 Windows 上的输出:

1
2
3
4
5
主目录: C:\Users\admin
配置目录: C:\Users\admin\.myapp
配置文件: C:\Users\admin\.myapp\config.json
临时目录: C:\Users\admin\AppData\Local\Temp
已创建配置目录: C:\Users\admin\.myapp

在 Linux 上的输出:

1
2
3
4
5
主目录: /home/admin
配置目录: /home/admin/.myapp
配置文件: /home/admin/.myapp/config.json
临时目录: /tmp
已创建配置目录: /home/admin/.myapp

跨平台工具函数库

在实际运维中,最常见的需求包括软件包管理、服务管理和用户管理。不同平台使用的底层命令各不相同:Windows 用 winget/choco,Linux 用 apt/yum/dnf,macOS 用 brew;服务管理在 Windows 上是 Get-Service,Linux 上是 systemctl,macOS 上是 launchctl。下面这个函数库将这些差异封装为统一的 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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#region 包管理器抽象

function Install-PlatformPackage {
[CmdletBinding(SupportsShouldProcess)]
param(
[Parameter(Mandatory)]
[string]$Name,

[Parameter()]
[switch]$Update
)

if ($IsWindows) {
# 优先使用 winget,回退到 choco
$winget = Get-Command winget -ErrorAction SilentlyContinue
if ($winget) {
$args = @('install', '--id', $Name, '--accept-package-agreements', '--accept-source-agreements')
if ($Update) { $args += '--upgrade' }
if ($PSCmdlet.ShouldProcess($Name, 'Install via winget')) {
& winget @args
}
}
else {
$choco = Get-Command choco -ErrorAction SilentlyContinue
if ($choco) {
if ($PSCmdlet.ShouldProcess($Name, 'Install via chocolatey')) {
& choco install $Name -y
}
}
else {
Write-Error "未找到可用的包管理器(winget 或 chocolatey)"
}
}
}
elseif ($IsMacOS) {
if ($PSCmdlet.ShouldProcess($Name, 'Install via brew')) {
if ($Update) {
& brew upgrade $Name
}
else {
& brew install $Name
}
}
}
elseif ($IsLinux) {
$pkgMgr = Get-LinuxPackageManager
if ($PSCmdlet.ShouldProcess($Name, "Install via $pkgMgr")) {
switch ($pkgMgr) {
'apt' { & sudo apt-get install -y $Name }
'dnf' { & sudo dnf install -y $Name }
'yum' { & sudo yum install -y $Name }
'pacman' { & sudo pacman -S --noconfirm $Name }
default { Write-Error "不支持的包管理器: $pkgMgr" }
}
}
}
}

function Get-LinuxPackageManager {
[CmdletBinding()]
param()

if (Get-Command apt-get -ErrorAction SilentlyContinue) { return 'apt' }
if (Get-Command dnf -ErrorAction SilentlyContinue) { return 'dnf' }
if (Get-Command yum -ErrorAction SilentlyContinue) { return 'yum' }
if (Get-Command pacman -ErrorAction SilentlyContinue) { return 'pacman' }
return $null
}

#endregion

#region 服务管理抽象

function Get-PlatformService {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Name
)

if ($IsWindows) {
return Get-Service -Name $Name -ErrorAction SilentlyContinue
}
elseif ($IsLinux) {
$status = systemctl is-active $Name 2>$null
$enabled = systemctl is-enabled $Name 2>$null
return [PSCustomObject]@{
Name = $Name
Status = if ($status -eq 'active') { 'Running' } else { 'Stopped' }
Enabled = ($enabled -eq 'enabled')
}
}
elseif ($IsMacOS) {
$loaded = launchctl list $Name 2>$null
return [PSCustomObject]@{
Name = $Name
Status = if ($loaded) { 'Running' } else { 'Stopped' }
Enabled = [bool]$loaded
}
}
}

#endregion

#region 用户管理抽象

function Get-PlatformUser {
[CmdletBinding()]
param(
[Parameter()]
[string]$UserName = $env:USER
)

if ($IsWindows) {
$adsi = [ADSI]"WinNT://$env:COMPUTERNAME"
$user = $adsi.Children | Where-Object {
$_.SchemaClassName -eq 'User' -and $_.Name -eq $UserName
}
if ($user) {
return [PSCustomObject]@{
Name = $user.Name[0]
FullName = $user.FullName[0]
Description = $user.Description[0]
HomeDir = Join-PlatformPath $env:USERPROFILE $user.Name[0]
Platform = 'Windows'
}
}
}
else {
$passwd = getent passwd $UserName 2>$null
if ($passwd) {
$fields = $passwd -split ':'
return [PSCustomObject]@{
Name = $fields[0]
FullName = ($fields[4] -split ',', 2)[0]
Description = ''
HomeDir = $fields[5]
Shell = $fields[6]
Platform = if ($IsMacOS) { 'macOS' } else { 'Linux' }
}
}
}
}

#endregion

# 使用示例
$platform = Get-PlatformInfo
Write-Host "`n===== 跨平台工具函数演示 ====="
Write-Host "平台: $($platform.Platform)"

$currentUser = Get-PlatformUser
Write-Host "当前用户: $($currentUser.Name)"
Write-Host "主目录: $($currentUser.HomeDir)"

$tempPath = Get-PlatformTempPath
Write-Host "临时目录: $tempPath"

在 Windows 上运行的输出:

1
2
3
4
5
===== 跨平台工具函数演示 =====
平台: Windows
当前用户: admin
主目录: C:\Users\admin\admin
临时目录: C:\Users\admin\AppData\Local\Temp

在 Linux 上运行的输出:

1
2
3
4
5
===== 跨平台工具函数演示 =====
平台: Linux
当前用户: admin
主目录: /home/admin
临时目录: /tmp

注意事项

  1. 始终使用 [System.IO.Path] 类处理路径拼接:不要手动拼接路径分隔符。[System.IO.Path]::Combine() 会自动根据当前平台选择正确的分隔符,从根本上避免路径格式错误。

  2. 利用 $PSVersionTable 做版本检查:跨平台脚本开头建议加上版本守卫,确保运行环境是 PowerShell 7+。例如 if ($PSVersionTable.PSVersion.Major -lt 7) { throw '此脚本需要 PowerShell 7.0 或更高版本' }

  3. 注意 cmdlet 的平台差异:并非所有 cmdlet 在所有平台上都可用。例如 Get-Service 只在 Windows 上可用,Set-Clipboard 在 Linux 上需要 xclip 依赖。编写脚本前用 Get-Command 检查目标 cmdlet 的可用性。

  4. 文件权限模型不同:Windows 使用 ACL(访问控制列表),Linux/macOS 使用 POSIX 权限(rwx)。如果脚本涉及权限设置,需要根据平台分别调用 icacls(Windows)或 chmod/chown(Linux/macOS)。

  5. 环境变量大小写敏感:Windows 的环境变量名不区分大小写,而 Linux/macOS 区分。$env:PATH 在所有平台上都能工作,但自定义环境变量如 $env:MyApp_Home 在 Linux 上必须与设置时完全一致(包括大小写)。

  6. 测试覆盖三大平台:推荐使用 Docker(Linux)、WSL(Linux 交叉测试)和本地 macOS 环境进行实际验证。仅靠阅读代码很难发现所有平台特定问题,自动化测试是最好的保障。有条件的话可以使用 GitHub Actions 的多平台矩阵(os: [windows-latest, ubuntu-latest, macos-latest])实现 CI 级别的跨平台验证。

PowerShell 技能连载 - 跨平台 PowerShell 实践

适用于 PowerShell 7.0 及以上版本(跨平台)

PowerShell 7 的跨平台能力是一个里程碑式的变化——同一套脚本语言可以在 Windows、Linux 和 macOS 上运行。对于管理混合环境的运维团队来说,这意味着只需学习一种语言就能管理所有平台。但跨平台并非”写一次到处运行”那么简单,不同操作系统的路径格式、包管理器、服务管理和服务发现机制都有差异。

本文将讲解跨平台 PowerShell 脚本的最佳实践,包括平台检测、路径处理、包管理适配和实用案例。

安装 PowerShell 7

在 Linux 上安装 PowerShell 7 比较简单,各主流发行版都有官方包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 检查当前平台信息
$PSVersionTable
Write-Host "操作系统:$( [System.Runtime.InteropServices.RuntimeInformation]::OSDescription )"
Write-Host "平台架构:$( [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture )"

# 平台检测变量
Write-Host "`n内置平台变量:"
Write-Host " `$IsWindows : $IsWindows"
Write-Host " `$IsLinux : $IsLinux"
Write-Host " `$IsMacOS : $IsMacOS"

# Ubuntu/Debian 安装命令(在 Bash 中运行)
# wget https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb
# sudo dpkg -i packages-microsoft-prod.deb
# sudo apt-get update && sudo apt-get install -y powershell

# CentOS/RHEL 安装
# sudo yum install -y https://packages.microsoft.com/config/rhel/8/packages-microsoft-prod.rpm
# sudo yum install -y powershell

# macOS 安装
# brew install --cask powershell

执行结果示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Name                           Value
---- -----
PSVersion 7.4.2
PSEdition Core
Platform Unix
OS Ubuntu 22.04.4 LTS

操作系统:Linux 5.15.0-101-generic #111-Ubuntu SMP
平台架构:X64

内置平台变量:
$IsWindows : False
$IsLinux : True
$IsMacOS : False

跨平台路径处理

路径分隔符是跨平台脚本最常见的陷阱。Windows 使用反斜杠 \,Linux/macOS 使用正斜杠 /。PowerShell 7 推荐使用 Join-PathSplit-Path 来处理路径:

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
# 错误做法:硬编码路径分隔符
# $path = "C:\Users\admin\Documents" # Windows only
# $path = "/home/admin/documents" # Linux only

# 正确做法:使用 Join-Path
$configDir = Join-Path $HOME "config"
$dataFile = Join-Path $configDir "data.json"
Write-Host "配置文件路径:$dataFile"

# 跨平台常用路径映射
function Get-PlatformPath {
param([string]$Segment)

$basePaths = @{
Config = if ($IsWindows) { Join-Path $env:APPDATA "MyApp" }
elseif ($IsMacOS) { Join-Path $HOME "Library" "Application Support" "MyApp" }
else { Join-Path $HOME ".config" "myapp" }
Data = if ($IsWindows) { Join-Path $env:LOCALAPPDATA "MyApp" }
elseif ($IsMacOS) { Join-Path $HOME "Library" "Application Support" "MyApp" "Data" }
else { Join-Path $HOME ".local" "share" "myapp" }
Temp = if ($IsWindows) { $env:TEMP }
else { "/tmp" }
Log = if ($IsWindows) { Join-Path $env:LOCALAPPDATA "MyApp" "Logs" }
else { Join-Path "/var" "log" "myapp" }
}

return $basePaths[$Segment]
}

foreach ($type in @('Config', 'Data', 'Temp', 'Log')) {
$path = Get-PlatformPath -Segment $type
Write-Host "$type => $path"
}

执行结果示例:

1
2
3
4
5
6
配置文件路径:/home/admin/config/data.json

Config => /home/admin/.config/myapp
Data => /home/admin/.local/share/myapp
Temp => /tmp
Log => /var/log/myapp

注意:在 PowerShell 7 中,即使 Windows 上也可以使用正斜杠 / 作为路径分隔符。但为了代码清晰,始终使用 Join-Path 是最佳实践。

跨平台服务管理

不同平台使用不同的服务管理器。以下是统一的跨平台服务管理函数:

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
52
53
54
55
56
57
58
function Get-ServiceStatus {
<#
.SYNOPSIS
跨平台获取服务状态
#>
param(
[Parameter(Mandatory)]
[string]$ServiceName
)

if ($IsWindows) {
$svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
if ($svc) {
return [PSCustomObject]@{
Name = $svc.Name
Status = $svc.Status.ToString()
StartType = $svc.StartType.ToString()
}
}
} else {
# Linux/macOS 使用 systemctl 或 launchctl
if (Get-Command systemctl -ErrorAction SilentlyContinue) {
$output = systemctl is-active $ServiceName 2>$null
$enabled = systemctl is-enabled $ServiceName 2>$null
return [PSCustomObject]@{
Name = $ServiceName
Status = if ($output -eq 'active') { 'Running' } else { 'Stopped' }
StartType = if ($enabled -eq 'enabled') { 'Automatic' } else { 'Manual' }
}
} elseif ($IsMacOS -and (Get-Command launchctl -ErrorAction SilentlyContinue)) {
$output = launchctl list 2>$null | Select-String $ServiceName
return [PSCustomObject]@{
Name = $ServiceName
Status = if ($output) { 'Running' } else { 'Stopped' }
StartType = 'Unknown'
}
}
}
}

# 跨平台重启服务
function Restart-PlatformService {
param([string]$ServiceName)

if ($IsWindows) {
Restart-Service -Name $ServiceName -Force
} elseif (Get-Command systemctl -ErrorAction SilentlyContinue) {
sudo systemctl restart $ServiceName
} elseif ($IsMacOS) {
sudo launchctl stop "com.$ServiceName"
sudo launchctl start "com.$ServiceName"
}
Write-Host "已重启服务:$ServiceName" -ForegroundColor Green
}

# 示例
Get-ServiceStatus -ServiceName 'ssh'
Get-ServiceStatus -ServiceName 'nginx'

执行结果示例:

1
2
3
4
5
6
7
Name Status StartType
---- ------ ---------
ssh Running Automatic

Name Status StartType
---- ------ ---------
nginx Running Automatic

跨平台系统信息采集

统一的系统信息采集函数,适配三个平台:

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
function Get-CrossPlatformSystemInfo {
<#
.SYNOPSIS
跨平台采集系统信息
#>

$info = [ordered]@{}

# 基础平台信息
$info['主机名'] = $env:COMPUTERNAME ?? hostname
$info['平台'] = if ($IsWindows) { 'Windows' } elseif ($IsMacOS) { 'macOS' } else { 'Linux' }
$info['PowerShell版本'] = $PSVersionTable.PSVersion.ToString()

if ($IsWindows) {
$os = Get-CimInstance Win32_OperatingSystem
$info['操作系统'] = $os.Caption
$info['版本'] = $os.Version
$info['总内存GB'] = [math]::Round($os.TotalVisibleMemorySize / 1MB, 2)
$info['可用内存GB'] = [math]::Round($os.FreePhysicalMemory / 1MB, 2)
$info['运行时间'] = ((Get-Date) - $os.LastBootUpTime).ToString('dd\天\ hh\:mm\:ss')

$cpu = Get-CimInstance Win32_Processor | Select-Object -First 1
$info['CPU'] = $cpu.Name
$info['CPU核心'] = $cpu.NumberOfLogicalProcessors
} else {
# Linux/macOS 系统信息
if ($IsLinux) {
$info['操作系统'] = (Get-Content /etc/os-release | Select-String '^PRETTY_NAME=') -replace 'PRETTY_NAME="?(.+)"?','$1'
} else {
$info['操作系统'] = sw_vers -productName
$info['版本'] = sw_vers -productVersion
}

# 内存信息
if ($IsLinux) {
$memInfo = Get-Content /proc/meminfo
$totalKB = ($memInfo | Select-String 'MemTotal:') -replace '\D',''
$freeKB = ($memInfo | Select-String 'MemAvailable:') -replace '\D',''
$info['总内存GB'] = [math]::Round($totalKB / 1MB, 2)
$info['可用内存GB'] = [math]::Round($freeKB / 1MB, 2)
}

# CPU 信息
if ($IsLinux) {
$cpuModel = (Get-Content /proc/cpuinfo | Select-String 'model name' | Select-Object -First 1) -replace 'model name\s+:\s+',''
$cpuCores = (Get-Content /proc/cpuinfo | Select-String 'processor' | Measure-Object).Count
$info['CPU'] = $cpuModel
$info['CPU核心'] = $cpuCores
}

# 运行时间
$uptimeOutput = uptime -p 2>$null
$info['运行时间'] = $uptimeOutput ?? "unknown"
}

# 磁盘信息(跨平台)
$info['磁盘'] = if ($IsWindows) {
Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" |
ForEach-Object {
[PSCustomObject]@{
驱动器 = $_.DeviceID
总GB = [math]::Round($_.Size / 1GB, 2)
可用GB = [math]::Round($_.FreeSpace / 1GB, 2)
}
}
} else {
df -h 2>/dev/null | Select-String '^/dev/' | ForEach-Object {
$parts = $_.ToString() -split '\s+'
[PSCustomObject]@{
驱动器 = $parts[5]
总GB = $parts[1] -replace '[A-Z]',''
可用GB = $parts[3] -replace '[A-Z]',''
}
}
}

return [PSCustomObject]$info
}

Get-CrossPlatformSystemInfo | Format-List

执行结果示例:

1
2
3
4
5
6
7
8
9
主机名        : ubuntu-server01
平台 : Linux
PowerShell版本 : 7.4.2
操作系统 : Ubuntu 22.04.4 LTS
总内存GB : 15.57
可用内存GB : 8.23
CPU : Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz
CPU核心 : 4
运行时间 : up 42 days, 3 hours, 17 minutes

跨平台包管理

不同平台有不同的包管理器。PowerShell 7 支持统一的 Install-Package 接口,但实际操作中往往需要调用原生包管理器:

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
function Install-CrossPlatformPackage {
<#
.SYNOPSIS
跨平台安装软件包
#>
param(
[Parameter(Mandatory)]
[string]$Package,

[ValidateSet('apt','yum','brew','winget','auto')]
[string]$Manager = 'auto'
)

if ($Manager -eq 'auto') {
if ($IsWindows) {
$Manager = 'winget'
} elseif (Get-Command apt-get -ErrorAction SilentlyContinue) {
$Manager = 'apt'
} elseif (Get-Command yum -ErrorAction SilentlyContinue) {
$Manager = 'yum'
} elseif (Get-Command brew -ErrorAction SilentlyContinue) {
$Manager = 'brew'
}
}

switch ($Manager) {
'apt' { sudo apt-get update; sudo apt-get install -y $Package }
'yum' { sudo yum install -y $Package }
'brew' { brew install $Package }
'winget' { winget install --id $Package --accept-source-agreements }
}

Write-Host "已通过 $Manager 安装:$Package" -ForegroundColor Green
}

# 批量安装常用工具
$commonTools = @('git', 'curl', 'wget')
foreach ($tool in $commonTools) {
Install-CrossPlatformPackage -Package $tool
}

执行结果示例:

1
2
3
已通过 apt 安装:git
已通过 apt 安装:curl
已通过 apt 安装:wget

注意事项

  1. **使用 Join-Path**:永远不要手动拼接路径,使用 Join-PathSplit-PathTest-Path 等命令确保跨平台兼容
  2. 平台检测使用内置变量$IsWindows$IsLinux$IsMacOS 是最可靠的检测方式,在 PowerShell 5.1 中需要手动检测 $env:OS
  3. 换行符差异:Windows 使用 CRLF(\r\n),Linux/macOS 使用 LF(\n)。处理文本文件时注意统一换行符
  4. 文件权限:Linux/macOS 使用 POSIX 权限模型(chmod),Windows 使用 ACL。跨平台脚本应分别处理
  5. 环境变量大小写:Windows 环境变量不区分大小写,Linux/macOS 区分大小写
  6. 测试覆盖:跨平台脚本应在所有目标平台上测试,不要假设”在 Linux 上能跑就行”