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

标签 swift nstask

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

问题是在某些 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 运行任何示例都会成功,因此与 NSTask 的交互有关,当从该特定 URL(和其他一些)检索时,以及当存在管道时,这会导致 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/36009175/

相关文章:

swift - 我如何判断 FileHandle 什么时候没有可读的内容?

ios - 另一个 "unrecognized selector sent to instance"

ios - 我如何从我的表格 View 中删除空单元格

objective-c - 使用 NSTask 更改 osx 网络配置

objective-c - 在 objective-C 中发送 control+c (SIGINT) 到 NSPIPE

objective-c - 如何使用 NSTask 授予权限 - objective-c

ios - 读取存储在 Xcode 项目中的文件

ios - Swift SpriteKit iOS - 碰撞 : didBeginContact for one sprite, 而不是其他人

ios - 是否可以将小部件中的数据写入主应用程序Coredata而不进行分组?

cocoa - NSTask 启动导致崩溃