You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PowerShell调用Process运行curl挂起及无法获取错误输出问题

问题根因

两种写法的故障原因都非常明确:

  • 原生System.Diagnostics.Process写法永久挂起是典型的流缓冲区死锁:你同时重定向了标准输出、标准错误流,但在进程完全退出前没有读取流内容。Windows给子进程分配的单流缓冲区大小通常只有4KB,当curl输出的内容(含正常进度、报错信息)填满缓冲区后,curl会被阻塞在流写入操作上无法退出,而你的代码正卡在WaitForExit()等待curl结束,两边互相等待就形成死锁。输出内容少的时候缓冲区没满,就会偶发运行正常。
  • Start-Process写法拿不到错误输出,是因为你调用时没有指定流重定向参数:默认配置下Start-Process不会捕获子进程的标准输出、标准错误流,进程对象的StandardError、StandardOutput属性自然始终为空。
解决方案

两种稳定方案都可以同时避免死锁、正常获取错误输出,按需选择即可。

方案1:修正原生Process类调用(无临时文件开销)

核心改动是在进程启动后立刻异步读取两个流的内容,从根源避免缓冲区写满阻塞:

$curlPath = "curl.exe"
$curlArgs = "sftp://download.url.com/filename_20220618.zip --user username:psw -o D:\filename_20220618.zip -k"
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $curlPath
$pinfo.Arguments = $curlArgs
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.CreateNoWindow = $true
$curlProcess = New-Object System.Diagnostics.Process
$curlProcess.StartInfo = $pinfo

# 初始化存储流内容的容器
$stdoutBuilder = New-Object System.Text.StringBuilder
$stderrBuilder = New-Object System.Text.StringBuilder
# 注册流接收回调
$curlProcess.add_OutputDataReceived({
    param($sender, $eventArgs)
    if ($null -ne $eventArgs.Data) { [void]$stdoutBuilder.AppendLine($eventArgs.Data) }
})
$curlProcess.add_ErrorDataReceived({
    param($sender, $eventArgs)
    if ($null -ne $eventArgs.Data) { [void]$stderrBuilder.AppendLine($eventArgs.Data) }
})

$curlProcess.Start() | Out-Null
# 启动异步流读取
$curlProcess.BeginOutputReadLine()
$curlProcess.BeginErrorReadLine()
$curlProcess.WaitForExit()
# 二次WaitForExit确保异步流读取完成
$curlProcess.WaitForExit()

# 提取结果
$exitCode = $curlProcess.ExitCode
$stdoutContent = $stdoutBuilder.ToString().Trim()
$stderrContent = $stderrBuilder.ToString().Trim()

# 业务逻辑示例:执行失败时打印错误
if ($exitCode -ne 0) {
    Write-Error "curl下载失败,退出码[$exitCode],错误信息:$stderrContent"
}

方案2:正确使用Start-Process(写法更简洁)

通过Start-Process自带的流重定向参数,将输出写入临时文件后读取,PowerShell内部会自动处理流读取逻辑,不会出现死锁:

$curlPath = "curl.exe"
$curlArgs = "sftp://download.url.com/filename_20220618.zip --user username:psw -o D:\filename_20220618.zip -k"
# 创建临时文件存储流内容
$stdoutTemp = [System.IO.Path]::GetTempFileName()
$stderrTemp = [System.IO.Path]::GetTempFileName()

try {
    $curlProcess = Start-Process -FilePath $curlPath -ArgumentList $curlArgs -Wait -PassThru `
        -WindowStyle Hidden -NoNewWindow `
        -RedirectStandardOutput $stdoutTemp -RedirectStandardError $stderrTemp
    
    $exitCode = $curlProcess.ExitCode
    $stdoutContent = Get-Content -Path $stdoutTemp -Raw -Encoding UTF8 | ForEach-Object { $_.Trim() }
    $stderrContent = Get-Content -Path $stderrTemp -Raw -Encoding UTF8 | ForEach-Object { $_.Trim() }

    if ($exitCode -ne 0) {
        Write-Error "curl下载失败,退出码[$exitCode],错误信息:$stderrContent"
    }
}
finally {
    # 执行完成后清理临时文件
    Remove-Item -Path $stdoutTemp, $stderrTemp -ErrorAction SilentlyContinue
}
注意事项

不要使用同步顺序读取流的写法(例如先调用$curlProcess.StandardOutput.ReadToEnd()再读取StandardError),如果错误流先填满缓冲区,这种写法依然会触发死锁。

内容的提问来源于stack exchange,提问作者DBZHOU

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 17:36:22