gpt4 book ai didi

swift - 如果存在管道,则通过 NSTask 的 cURL 不会终止

转载 作者:行者123 更新时间:2023-11-30 12:27:32 25 4
gpt4 key购买 nike

我正在尝试在 Swift 中同步读取简单命令行批处理脚本的 URL 内容。为了简单起见,我使用 cURL - 我知道如果必须的话我可以使用 NSURLSession。我还在 OSX 上使用 Swift 的开源版本通过 swift build 来构建它。

问题是,在某些 URL 上,如果 stdout 已重定向到管道,则 NSTask 永远不会终止。

// This will hang, and when terminated with Ctrl-C reports "(23) Failed writing body"
import Foundation
let task = NSTask()
let pipe = NSPipe()
task.launchPath = "/usr/bin/curl"
task.arguments = ["http://trove.nla.gov.au/newspaper/page/21704647"]
task.standardOutput = pipe
task.launch()
task.waitUntilExit()

但是,如果您删除管道或更改 URL,任务就会成功。

// This will succeed - no pipe
import Foundation
let task = NSTask()
task.launchPath = "/usr/bin/curl"
task.arguments = ["http://trove.nla.gov.au/newspaper/page/21704647"]
task.launch()
task.waitUntilExit()

// This will succeed - different URL
import Foundation
let task = NSTask()
let pipe = NSPipe()
task.launchPath = "/usr/bin/curl"
task.arguments = ["http://trove.nla.gov.au/newspaper/page/21704646"]
task.standardOutput = pipe
task.launch()
task2.waitUntilExit()

直接从终端使用curl运行任何示例都会成功,因此当从特定URL(和其他一些)检索时,以及当存在管道时,与NSTask的交互存在一些问题,这会导致cURL失败。

最佳答案

稍微扩展一下@Hod的答案:启动的标准输出进程被重定向到管道,但您的程序永远不会从管道中读取另一管端。管道的缓冲区有限,例如 How big is the pipe buffer?这说明 macOS 上的管道缓冲区大小(最多)为 64KB。

如果管道缓冲区已满,则启动的进程无法在其上写入不再了。如果进程使用阻塞 I/O,则管道的 write() 将阻塞,直到至少可以写入一个字节。确实如此在你的情况下永远不会发生,所以进程挂起并且不会终止。

仅当写入标准输出的数量时才会出现此问题超出了管道缓冲区大小,这解释了为什么它只发生在某些 URL 上,而不会发生在其他 URL 上。

作为解决方案,您可以从管道中读取,例如与

let data = pipe.fileHandleForReading.readDataToEndOfFile()

等待进程终止之前。另一种选择是使用异步读取,例如代码来自Real time NSTask output to NSTextView with Swift :

pipe.fileHandleForReading.readabilityHandler = { fh in
let data = fh.availableData
// process data ...
}

这也允许读取标准输出和标准错误通过管道从进程中无阻塞。

关于swift - 如果存在管道,则通过 NSTask 的 cURL 不会终止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43985659/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com