我有一个 PowerShell 脚本,可以在多个服务器上远程执行脚本。该脚本检查每个服务器上是否存在特定脚本,如果存在,则远程调用该脚本。我想优化脚本的性能和内存使用情况,特别是在处理大量服务器时。以下是我当前使用的代码。
服务器列表和初始化:
服务器列表 (
$servers
) 在脚本的开头定义。它保存将执行脚本的远程服务器的名称或 IP 地址。我们还初始化了几个变量,例如 $results
来存储结果,$missingScriptServers
用于缺少脚本的服务器,以及成功和失败的计数器($successCount
和 $failureCount
)。
检查脚本是否存在:
函数
Check-ScriptExists
检查每个远程服务器上是否存在指定的脚本 ($scriptPath
)。它使用 PowerShell 远程处理(New-PSSession
和 Invoke-Command
)来检查远程服务器上是否存在脚本文件。如果脚本存在,函数返回$true
;否则,返回 $false
。
远程调用脚本:
Invoke-ScriptOnServer
函数负责在服务器上远程执行脚本。如果脚本存在,该函数将使用 Invoke-Command
执行它。如果执行过程中出现错误,则返回1
表示失败。
远程命令并行执行:
该脚本检查列表中的每个服务器。对于存在脚本的服务器,它使用
Start-Job
创建后台作业。这允许脚本在多个服务器上并行调用远程脚本,从而无需等待每个服务器完成后再转到下一个服务器,从而提高了性能。
收集和显示结果:
启动远程执行作业后,脚本使用
Wait-Job
等待每个作业完成,并使用 Receive-Job
检索结果。然后,它会使用 Remove-Job
删除作业以释放资源。结果存储在 $results
中,成功和失败计数相应更新。最后,脚本以表格格式输出结果并显示成功/失败计数。
# Define the list of server names or IP addresses
$servers = @(
"server1",
"server2",
"server3"
# Add more servers as needed
)
# Path to the script on the servers
$scriptPath = "D:\your_script.ps1"
# Initialize arrays to hold result objects and counts for success and failure
$results = @()
$missingScriptServers = @()
$successCount = 0
$failureCount = 0
# Function to check if the script exists on the server
function Check-ScriptExists {
param (
[string]$Server,
[string]$ScriptPath
)
try {
$session = New-PSSession -ComputerName $Server
$exists = Invoke-Command -Session $session -ScriptBlock {
param($ScriptPath)
Test-Path -Path $ScriptPath
} -ArgumentList $ScriptPath
Remove-PSSession -Session $session
return $exists
} catch {
return $false
}
}
# Function to invoke the script on a remote server
function Invoke-ScriptOnServer {
param (
[string]$Server,
[string]$ScriptPath
)
try {
$session = New-PSSession -ComputerName $Server
$response = Invoke-Command -Session $session -ScriptBlock {
param($ScriptPath)
& $ScriptPath
return $LASTEXITCODE
} -ArgumentList $ScriptPath
Remove-PSSession -Session $session
return $response
} catch {
return 1 # Return failure exit code in case of error
}
}
# Check for script existence and start jobs in parallel
$jobs = @()
foreach ($server in $servers) {
$scriptExists = Check-ScriptExists -Server $server -ScriptPath $scriptPath
if ($scriptExists) {
$jobs += Start-Job -ScriptBlock {
param($server, $scriptPath)
Invoke-ScriptOnServer -Server $server -ScriptPath $scriptPath
} -ArgumentList $server, $scriptPath
} else {
$missingScriptServers += $server
}
}
# Wait for all jobs to complete and collect results
foreach ($job in $jobs) {
$job | Wait-Job
$response = $job | Receive-Job
$job | Remove-Job
# Update success/failure count based on the response code
if ($response -eq 0) {
$successCount++
} else {
$failureCount++
}
$results += [pscustomobject]@{ Server = $server; Response = $response }
}
# Display the results in a table format
$results | Format-Table -AutoSize
# Display the success and failure counts
Write-Output "Number of servers succeeded: $successCount"
Write-Output "Number of servers failed: $failureCount"
# Display the servers where the script is missing
if ($missingScriptServers.Count -gt 0) {
Write-Output "The script is missing on the following servers:"
$missingScriptServers | Format-Table -AutoSize
} else {
Write-Output "The script is available on all specified servers."
}
# Define the list of server names or IP addresses
$servers = @(
"server1",
"server2",
"server3"
# Add more servers as needed
)
# Path to the script on the servers
$scriptPath = "D:\your_script.ps1"
# Initialize arrays to hold result objects and counts for success and failure
$results = @()
$missingScriptServers = @()
$successCount = 0
$failureCount = 0
# Function to check if the script exists on the server
function Check-ScriptExists {
param (
[string]$Server,
[string]$ScriptPath
)
try {
$session = New-PSSession -ComputerName $Server
$exists = Invoke-Command -Session $session -ScriptBlock {
param($ScriptPath)
Test-Path -Path $ScriptPath
} -ArgumentList $ScriptPath
Remove-PSSession -Session $session
return $exists
} catch {
return $false
}
}
# Function to invoke the script on a remote server
function Invoke-ScriptOnServer {
param (
[string]$Server,
[string]$ScriptPath
)
try {
$session = New-PSSession -ComputerName $Server
$response = Invoke-Command -Session $session -ScriptBlock {
param($ScriptPath)
& $ScriptPath
return $LASTEXITCODE
} -ArgumentList $ScriptPath
Remove-PSSession -Session $session
return $response
} catch {
return 1 # Return failure exit code in case of error
}
}
# Check for script existence and start jobs in parallel
$jobs = @()
foreach ($server in $servers) {
$scriptExists = Check-ScriptExists -Server $server -ScriptPath $scriptPath
if ($scriptExists) {
$jobs += Start-Job -ScriptBlock {
param($server, $scriptPath)
Invoke-ScriptOnServer -Server $server -ScriptPath $scriptPath
} -ArgumentList $server, $scriptPath
} else {
$missingScriptServers += $server
}
}
# Wait for all jobs to complete and collect results
foreach ($job in $jobs) {
$job | Wait-Job
$response = $job | Receive-Job
$job | Remove-Job
# Update success/failure count based on the response code
if ($response -eq 0) {
$successCount++
} else {
$failureCount++
}
$results += [pscustomobject]@{ Server = $server; Response = $response }
}
# Display the results in a table format
$results | Format-Table -AutoSize
# Display the success and failure counts
Write-Output "Number of servers succeeded: $successCount"
Write-Output "Number of servers failed: $failureCount"
# Display the servers where the script is missing
if ($missingScriptServers.Count -gt 0) {
Write-Output "The script is missing on the following servers:"
$missingScriptServers | Format-Table -AutoSize
} else {
Write-Output "The script is available on all specified servers."
}
作为一般规则,避免使用
+=
Invoke-Command
已经并行运行。此外,无需检查脚本是否作为单独的调用存在,只需尝试运行它并解析输出即可。也可以使用Invoke-Command -FilePath
直接用命令执行脚本
# Define the list of server names or IP addresses
$servers = @(
"server1",
"server2",
"server3"
# Add more servers as needed
)
# Path to the script on the servers
$scriptPath = "D:\your_script.ps1"
$icmParams = @{
ComputerName = $servers
FilePath = $scriptPath
}
$results = Invoke-Command @icmParams
# Format, filter, and display the $results to your liking