Swift数据管道Pipe()
Swift数据管道Pipe()

Swift数据管道Pipe()

Pipe() 是 Swift(Foundation 框架)中的一个类,用于在程序和子进程之间创建双向通信通道,本质上是一个数据管道,可以用来:

1、读取子进程的输出(standardOutput)

2、捕获子进程的错误信息(standardError)

3、向子进程写入数据(standardInput)

Pipe 是 Foundation 提供的类,背后基于 Unix 的底层 pipe() 系统调用。

可以把Pipe理解为一个中转通道,把它插在 Process(进程命令)的输出口上,它就能把进程中的内容“接管”出来,供使用 Swift 来读取或处理。

常用属性和方法

1、fileHandleForReading:用来从管道中读取数据(子进程输出)。

process.standardOutput = pipe
let fileHandle = pipe.fileHandleForReading
let logData = fileHandle.readDataToEndOfFile()
if let log = String(data: logData, encoding: .utf8) {
    print("pngquant 日志:\n\(log)")
}

这是子进程中读取输出的主要手段,通常用来绑定 standardOutput 或 standardError:

2、fileHandleForWriting:用来写入数据(发送到子进程输入)。

process.standardInput = pipe
let input = "Hello\n".data(using: .utf8)!
pipe.fileHandleForWriting.write(input)

适合场景:子进程是 read, cat, python 这类能从 stdin 读取的命令。

3、fileHandleForReading.readDataToEndOfFile():用于同步读取全部数据,直到文件句柄关闭(也就是子进程退出)。

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

适合输出少、命令执行时间短的场景;

长时间运行或输出很多时,可能造成阻塞或死锁。

4、fileHandleForReading.readabilityHandler:监听数据到达事件(异步读取),每当有数据可读,系统就自动触发这个闭包。

pipe.fileHandleForReading.readabilityHandler = { handle in
    let data = handle.availableData
    if data.isEmpty {
        print("数据读取完毕")
    } else if let line = String(data: data, encoding: .utf8) {
        print("实时输出:\(line)")
    }
}

非阻塞,适合实时显示子进程的输出(如进度、log)。

注意:需要要主动关闭它,否则句柄不会释放:

pipe.fileHandleForReading.readabilityHandler = nil

5、availableData:用于在 readabilityHandler 中获取当前缓冲区内容。

let data = pipe.fileHandleForReading.availableData

6、write(_:):向 fileHandleForWriting 写入数据。

let input = "exit\n".data(using: .utf8)!
pipe.fileHandleForWriting.write(input)

7、closeFile():手动关闭 fileHandle,尤其在写入完毕后必须关闭,表示 EOF:

pipe.fileHandleForWriting.closeFile()

如果不关闭,有些子进程(如 cat)会一直等待输入,导致卡住。

8、readToEnd()(async/await):在 macOS 12+ 中,FileHandle 支持 async 方式读取:

if let data = try? await pipe.fileHandleForReading.readToEnd(),
   let output = String(data: data, encoding: .utf8) {
    print("读取到:\(output)")
}

支持现代 async/await,但需要 macOS 12+。

使用场景

1、获取命令行输出:

let pipe = Pipe()
process.standardOutput = pipe

try process.run()

let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)
print("输出结果:\(output ?? "无")")

2、同时捕获错误输出:

process.standardError = pipe // 错误输出也导入 pipe

3、向子进程输入内容

let inputPipe = Pipe()
process.standardInput = inputPipe

let input = "hello\n".data(using: .utf8)!
inputPipe.fileHandleForWriting.write(input)

注意事项

管道是有缓冲区的,不能无限写入或读取;

如果用 readabilityHandler(异步监听),要注意线程和内存释放;

要确保读取在子进程退出之后完成,防止丢数据;

// Process()异步完成后的回调函数
process.terminationHandler = { process in
    let logData = fileHandle.readDataToEndOfFile()
    // 在完成后,输出捕获的信息,防止丢失数据
    if let log = String(data: logData, encoding: .utf8) {
        print("pngquant 日志:\n\(log)")
    }
}

如果既捕获标准输出又捕获错误输出,通常会用两个 Pipe()(除非想统一输出)。

总结

Pipe() 是连接子进程的“数据管道”,可以读到或写入子进程中的内容。它是 Process 的好搭档,尤其适用于图片压缩、脚本执行、终端命令调用等场景。

相关文章

Swift执行进程命令Process():https://fangjunyu.com/2025/07/15/swift%e6%89%a7%e8%a1%8c%e8%bf%9b%e7%a8%8b%e5%91%bd%e4%bb%a4process/

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注