当前位置: 首页 > 面试题库 >

通过NSTask的cURL不会终止是否存在管道

闾丘朗
2023-03-14
问题内容

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

问题在于,如果已将stdout重定向到管道,则在某些URL上,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()

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


问题答案:

在@Hod的答案上稍作扩展:已启动进程的标准输出被重定向到管道,但是您的程序从不从另一管道末端读取。管道的 缓冲区有限, 例如参见
管道缓冲区有多大? 这说明macOS上的管道缓冲区大小最大为64KB。

如果管道缓冲区已满,则启动的进程无法再对其进行写操作。如果进程使用阻塞的I /
O,则write()管道的阻塞将一直阻塞,直到可以写入至少一个字节为止。在您的情况下,这永远不会发生,因此该过程将挂起并且不会终止。

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

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

let data = pipe.fileHandleForReading.readDataToEndOfFile()


等待过程终止之前。另一个选择是使用异步读取,例如使用从Swift实时NSTask输出到NSTextView的代码:

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

这也将允许通过管道从过程中读取标准输出和标准错误而不会阻塞。



 类似资料:
  • 问题内容: 我使用Hibernate创建了一个程序。 程序到达主要功能端,但是程序正在运行。 我不知道使用Hibernate 4.x版进行配置时是否会发生这种情况。 配置方式错误吗? manual1_1_first_hibernate_apps.java 实用程序 程序终止并使用buildSessionFactory方法时,以下控制台日志片段。 但是,如果不使用已弃用的buildSessionFa

  • 问题内容: 我(几乎)成功地将Node.js与Express和Redis结合使用来处理会话。 我遇到的问题是使用时不保留会话。 这是我的看到方式: console.log()打印: 现在,这是以下代码: 这个console.log()打印出: 显然,“用户名”对象已消失。该会话没有保留它,而是重新构建了一个新会话。 我该如何解决?如果您需要任何信息,请不要犹豫。 这是我设置会话管理的代码: 这是基

  • 我在负责同步的类中使用PublishSubject。当同步完成时,将通知所有订阅服务器。发生错误时也会发生同样的情况。我注意到,下次我在发生错误后订阅时,它会立即返回给订阅者。 因此类可能如下所示: 是否有一个可观察的东西可以作为代理?可能是其他Rx方法? 更新:我遵循了@Akarnokd方法,发出包装到RxJava中的事件。然后通过展开它们。因此类的客户端不需要执行此操作。

  • 问题内容: 我可以使用成功启动,文档中提到可以正常退出应用程序hit ctrl-c。 Maven进程确实终止了,但是Tomcat仍在运行,我仍然可以访问该网页。当我再次尝试启动spring-boot时,由于端口正在使用中,因此无法启动Tomcat。 要继续,我必须手动终止正在运行的进程。这是一个错误还是我错过了什么? 问题答案: 我仍然在Windows 7上运行的1.1.9版本上碰巧。 因此,在按

  • 要继续,我必须手动终止正在运行的进程。这是一个窃听器还是我遗漏了什么?

  • 我的理解是,C中的严格别名是在basic中定义的。11级: (11) 如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义: < li>(11.1)对象的动态类型, < li>(11.2)对象动态类型的cv限定版本, < li>(11.3)与对象的动态类型类似的类型(在conv.qual中定义), < li>(11.4)对应于对象动态类型的有符号或无符号类型, < li>