Swift:使用管道缓冲`Process`的很长的参数

时间:2019-02-05 10:53:36

标签: swift nspipe

问题

我正试图从我的Swift可执行文件中调用Python函数。

这很有效,直到参数变得太长并且我开始收到错误为止:

  

未捕获的异常NSInternalInconsistencyException
  原因:“无法posix_spawn:错误7”

从本质上讲,这意味着我正在发送用于处理的python代码(我的参数)的数据太长。

最后的选择

最明显的解决方案是将数据写入文件,然后向脚本发送包含该数据的文件路径。这会带来明显的性能限制。

一个可能更好的选择

这是我需要的帮助:我认为可能缓冲这些数据,而不是全部发送一个数据块。

我尝试用多种不同的关键字组合进行研究,并且过去一天一直在线阅读许多文章,但没有一个回答我的问题:是否可以缓冲数据以避开内核强加{{1 }}使用ARG_MAXProcess限制大约260000字节?

代码

因为它总是可以帮助我查看当前用于调用Python函数的代码,这就是我所拥有的:

Pipe

再过一会儿:

@discardableResult
public static func shell(_ args: String...) -> String {

    let task = Process()
    task.launchPath = "/usr/bin/env"
    task.arguments = args
    let pipe = Pipe()
    task.standardOutput = pipe
    task.launch()
    task.waitUntilExit()

    // return task.terminationStatus
    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output: String = String(data: data, encoding: String.Encoding.utf8)!

    return output
}   
//  public static func shell(_ args: String...) -> String {}

1 个答案:

答案 0 :(得分:0)

您可以使用 pipe 将大型有效负载发送到脚本的标准输入。当然,假设只有一个单个参数引起了您的问题(即,您仍然可以使用常规命令行参数继续发送的所有其他信息)。

例如:

func shell(stdin input: String, _ args: String...) -> String {
    let task = Process()
    task.launchPath = "/usr/bin/env"
    task.arguments = args

    let inPipe = Pipe()
    task.standardInput = inPipe
    inPipe.fileHandleForWriting.write(input.data(using: .utf8)!)
    inPipe.fileHandleForWriting.closeFile()

    let outPipe = Pipe()
    task.standardOutput = outPipe

    task.launch()
    task.waitUntilExit()

    let data = outPipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: data, encoding: .utf8)!

    return output
}

快速测试:

func bigString() -> String {
    var str = ""
    for i in 0..<50_000 {
        str.append("\(i % 10)")
    }
    return str
}

let script = "/PATH/TO/SCRIPT/echo.py"
let input = bigString()
let result = shell(stdin: input, "python", script, "some arg")
print(result)

打印:

  

ARG:ABC
  STDIN:012345678901234567890123 ...

echo.py脚本仅是:

import sys
print "ARG:", sys.argv[1]
print "STDIN:", sys.stdin.read()