我试图在Swift中为一个简单的命令行批处理脚本同步读取URL的内容.为了简单起见,我使用cURL – 我知道如果必须的话,我可以使用NSURLSession.我也在使用OSX上的Swift开源版本进行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直接运行任何示例都会成功,因此在从特定URL(以及其他一些URL)检索时,与NSTask的交互有一些内容,并且当存在管道时,这会导致cURL失败.
扩展@Hod的回答:推出的标准输出
进程被重定向到管道,但你的程序永远不会从中读取
其他管端.管道具有有限的缓冲区,例如参见
How big is the pipe buffer?
这解释了macOS上的管道缓冲区大小(最多)是64KB.
进程被重定向到管道,但你的程序永远不会从中读取
其他管端.管道具有有限的缓冲区,例如参见
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 ... }