我看到了这个线程,想分享我的解决方案给未来可能需要的人。这个解决方案是在 PowerShell Core 7.3.4 上运作的。
function Start-Command {
[cmdletbinding(DefaultParameterSetName="default")]
param (
[parameter(Mandatory,
Position=0,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[string]$Name,
[parameter(Mandatory=$false,
Position=1,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[object]$Arguments,
[parameter(Mandatory=$false,
ValueFromPipelineByPropertyName)]
[ValidateScript({Test-Path $_})]
[string]$WorkingDirectory,
[parameter(Mandatory=$false)]
[ValidateScript({
if ($PSVersionTable.Platform -eq "Unix") {
Throw "-LoadUserProfile cannot be used on Unix/Linux."
}
})]
[switch]$LoadUserProfile,
[parameter(Mandatory,
ValueFromPipelineByPropertyName,
ParameterSetName="timer")]
[ValidateRange(1, 600000)]
[int]$Timer,
[parameter(Mandatory=$false,
ValueFromPipelineByPropertyName)]
[ValidateScript({
if ($PSVersionTable.Platform -eq "Unix") {
Throw "-Verb cannot be used on Unix/Linux."
}
})]
[string]$Verb,
[parameter(Mandatory=$false)]
[switch]$Passthru
)
begin {
$FileName = (Get-Command -Name $Name -ErrorAction SilentlyContinue).Source
if ($null -eq $FileName -or $FileName -eq "") {
$getPSCommand = (Get-Command -Name $Name -ErrorAction SilentlyContinue)
if ($null -eq $getPSCommand -or $getPSCommand -eq "") {
Throw "Start-Command: Could not find command $Name nor could we find its PowerShell equivalent."
}
if ($getPSCommand.CommandType -eq 'Alias') {
Throw "Start-Command: This function does not support Aliases. Command $Name matches $($getPSCommand.ResolvedCommand.Name)."
}
if ($getPSCommand.Source -like "Microsoft.PowerShell*") {
Throw "Start-Command: This function should only be used for Non-PowerShell commands (e.g., wget, touch, mkdir, etc.)"
}
$FileName = $PSVersionTable.PSEdition -eq 'Core' ? (Get-Command -Name 'pwsh').Source : (Get-Command -Name 'powershell').Source
$Arguments = "-noprofile -Command `"& {$($getPSCommand.ReferencedCommand.Name) $Arguments}`""
}
$dataObject = [pscustomobject]@{
Title = $Name
OutputStream = ''
OutputData = ''
ErrorData = ''
ExitCode = 0
}
}
process {
$processStartInfoProps = @{
Arguments = $null -ne $Arguments ? $Arguments : $null
CreateNoWindow = $true
ErrorDialog = $false
FileName = $FileName
RedirectStandardError = $true
RedirectStandardInput = $true
RedirectStandardOutput = $true
UseShellExecute = $false
WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
WorkingDirectory = $PSBoundParameters.ContainsKey('WorkingDirectory') ? $WorkingDirectory : $PSScriptRoot
Verb = $PSBoundParameters.ContainsKey('Verb') ? $Verb : $null
}
if ($PSBoundParameters.ContainsKey('LoadUserProfile')) {
$processStartInfoProps.Add('LoadUserProfile', $LoadUserProfile)
}
try {
$process = New-Object System.Diagnostics.Process
$process.EnableRaisingEvents = $true
$processStartInfo = New-Object System.Diagnostics.ProcessStartInfo -Property $processStartInfoProps
$process.StartInfo = $processStartInfo
$outputEventParams = @{
InputObject = $process
SourceIdentifier = 'OnOutputDataReceived '
EventName = 'OutputDataReceived'
Action = {
param (
[System.Object]$sender,
[System.Diagnostics.DataReceivedEventArgs]$e
)
foreach ($data in $e.Data) {
if ($null -ne $data -and $data -ne "") {
$($data).Trim()
}
}
}
}
$dataObject.OutputStream = Register-ObjectEvent @outputEventParams
if ($process.Start()) {
$process.BeginOutputReadLine()
$dataObject.ErrorData = $process.StandardError.ReadToEnd()
if ($PSCmdlet.ParameterSetName -eq 'timer') {
$process.WaitForExit($Timer) | Out-Null
} else {
$process.WaitForExit()
}
}
$dataObject.ExitCode = $process.ExitCode
$dataObject.OutputData = Receive-Job -id $($dataObject.OutputStream.id)
[bool]$hasError = ($null -ne $($dataObject.ErrorData) -and $($dataObject.ErrorData) -ne "" -and $($dataObject.ExitCode) -ne 0) ? $true : $false
[bool]$hasOutput = ($null -ne $($dataObject.OutputData) -and $($dataObject.OutputData) -ne "") ? $true : $false
if ($Passthru) {
if ($hasError) {
$dataObject.ErrorData = $($dataObject.ErrorData.Trim())
}
$dataObject
} else {
if ($hasError) {
if ($($ErrorActionPreference) -ne 'Stop') {
Write-Error "Exit Code $($dataObject.ExitCode): $($dataObject.ErrorData.Trim())"
} else {
Throw "Exit Code $($dataObject.ExitCode): $($dataObject.ErrorData.Trim())"
}
}
if ($hasOutput) {
$($dataObject.OutputData)
}
}
}
finally {
$process.Close()
Unregister-Event -SourceIdentifier $($dataObject.OutputStream.Name) -Force | Out-Null
Remove-Job -Id $($dataObject.OutputStream.Id) -Force
}
}
}
示例1:常规用法
Start-Command -Name 'docker' -Arguments 'container ls --all'
示例2:逗号分隔的参数
Start-Command -Name 'docker' -Arguments 'container', 'ls', '--all'
示例3:Passthru用法
$whoami = Start-Command -Name 'whoami' -Passthru
$whoami
Title : whoami
OutputStream : System.Management.Automation.PSEventJob
OutputData : zac
ErrorStream :
ErrorData :
ExitCode : 0
示例4:错误示例
Start-Command -Name 'docker' -Arguments 'force' -ErrorAction Stop
Output:
Line |
245 | … Throw "Exit Code $($dataObject.ExitCode): $($dataObject.E …
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Exit Code 1: docker: 'force' is not a docker command. See 'docker --help'
start-process -nonewwindow cmd '/c timeout 5 & echo hi'
这个怎么样? - js2010$process.WaitForExit()
。我不得不将这行代码替换为while ( -Not $process.HasExited ) { sleep 1 }
,以获得相同的效果同时启用流式传输。 - Blaisem