Swift执行进程命令Process()
Swift执行进程命令Process()

Swift执行进程命令Process()

Process() 是 Swift(或者更准确地说是 Foundation 框架)中用来在macOS 或其他 Unix 系统中启动和控制子进程的类。

它允许 Swift 程序中执行系统命令,比如运行终端命令、调用外部工具、执行 shell 脚本等等。

导入Foundation框架使用:

import Foundation

Process可以简单的理解为程序内嵌的“终端”,可以运行二进制工具,执行shell脚本文件等命令。

基本用法

import Foundation

let process = Process()
process.executableURL = URL(fileURLWithPath: "/bin/echo")
process.arguments = ["Hello, World!"]

do {
    try process.run()
    process.waitUntilExit()
} catch {
    print("运行失败:\(error)")
}

这段代码等价于在终端中运行:

/bin/echo "Hello, World!"

常用属性

1、executableURL:URL?类型,要运行的命令路径(必须设置)。

process.executableURL = URL(fileURLWithPath: "/usr/bin/env")

在Xcode中运行二进制文件,可以使用Bundle.main.path获取命令路径:

guard let pngquant = Bundle.main.path(forResource: "pngquant", ofType: nil) else { return }
let process = Process()
process.executableURL = URL(fileURLWithPath: pngquant)  // 使用 Bundle 中的二进制文件路径

2、arguments:[String]?类型,命令行参数,传给可执行文件的参数。

process.arguments = ["ls", "-la", "/Users"]

3、standardOutput:Any?类型,设置子进程的标准输出,可以设为 Pipe() 获取输出,或设为 nil 使用默认行为(输出到终端)。

let pipe = Pipe()
process.standardOutput = pipe

配合Pipe()使用,获取输出信息。

4、standardInput:Any?类型,子进程的输入通道,也可用 Pipe() 或 FileHandle 传输数据到子进程。

let inputPipe = Pipe()
process.standardInput = inputPipe

5、standardError:Any?类型,错误输出流,通常设置为 Pipe() 来捕获错误信息。

let errorPipe = Pipe()
process.standardError = errorPipe

6、environment:[String: String]?类型,用于设置子进程的环境变量。默认继承当前进程的环境变量。

process.environment = ["PATH": "/usr/local/bin"]

7、currentDirectoryURL:URL?类型,子进程启动时的工作目录。默认是当前进程的工作目录。

process.currentDirectoryURL = URL(fileURLWithPath: "/Users/fangjunyu/Desktop")

8、terminationStatus:Int32类型,子进程退出后的状态码,通常 0 表示成功。

注意:只能在 waitUntilExit() 之后访问。

process.waitUntilExit()
print("状态码: \(process.terminationStatus)")

9、isRunning:检查子进程是否正在运行。

if process.isRunning {
    print("进程仍在运行")
}

10、terminationHandler:当子进程结束后触发的回调,用于异步处理。

process.terminationHandler = { proc in
    print("进程已结束,状态码:\(proc.terminationStatus)")
}

使用示例

1、Swift调用pngquant命令行工具

let process = Process()
process.executableURL = URL(fileURLWithPath: pngquant)
process.arguments = ["--quality=\(quality)", "--output", outputURL.path, inputURL.path]

let pipe = Pipe()
process.standardOutput = pipe
process.standardError = pipe

do {
    try process.run()   // 启动
    process.waitUntilExit() // 等待直到退出
    
    let logData = pipe.fileHandleForReading.readDataToEndOfFile()  // 获取压缩数据的日志
    if let log = String(data: logData, encoding: .utf8) {
        print("pngquant 日志:\n\(log)")
    }
    
    if process.terminationStatus == 0 {
        print("压缩完成")
        return
    } else {
        print("压缩失败,退出码:\(process.terminationStatus)")
        return
    }
} catch {
    print("运行 pngquant 失败:\(error)")
}

1、创建子进程对象

let process = Process()

创建一个新的子进程对象,用来执行系统命令或外部工具。

2、设置外部命令路径

process.executableURL = URL(fileURLWithPath: pngquant)

设置要执行的外部命令的路径,例如 “/usr/local/bin/pngquant”。

这里的pngquant实际上是从Bundle传入的:

guard let pngquant = Bundle.main.path(forResource: "pngquant", ofType: nil) else { return }

3、传递命令行参数

process.arguments = ["--quality=\(quality)", "--output", outputURL.path, inputURL.path]

设置传递给 pngquant 的命令行参数。相当于执行终端命令:

pngquant --quality=65-80 --output /输出路径.png /输入路径.png

4、设置子进程标准输出和错误输出

let pipe = Pipe()
process.standardOutput = pipe
process.standardError = pipe

设置子进程的标准输出和错误输出,都导向这个 pipe。这样就可以读取 pngquant 的输出日志(包括正常输出和错误信息)。

5、启动子进程

try process.run()

如果路径不合法、权限不足等,run() 会抛出异常,所以用了 try。

6、等待子进程

process.waitUntilExit()

等待子进程运行完成。这是一个阻塞操作,直到 pngquant 压缩结束。

7、读取输出内容

let logData = pipe.fileHandleForReading.readDataToEndOfFile()

从管道中读取完整输出内容。这个输出可能是压缩的进度、警告或错误信息。

8、输出日志

if let log = String(data: logData, encoding: .utf8) {
    print("pngquant 日志:\n\(log)")
}

将日志转换为字符串,便于开发调试或记录。

9、子进程退出码

if process.terminationStatus == 0 {
    // 成功
    completion(true)
} else {
    // 失败
    completion(false)
}

terminationStatus 是子进程退出码:

0 表示正常完成(压缩成功)。

非 0 表示有错误(如文件不存在、压缩失败)。

常见用途

1、运行系统命令(如 zip、cp、open)。

2、调用图像压缩工具(如 pngquant)。

3、启动后台任务。

4、自动化脚本调用。

总结

使用Process()命令,可以在Swift中执行终端的命令,在实际应用中,可以运行二进制工具,从而实现更多的功能。

相关文章

1、Swift压缩PNG图像:https://fangjunyu.com/2025/07/14/swift%e5%8e%8b%e7%bc%a9png%e5%9b%be%e5%83%8f/

扩展知识

1、waitUntilExit和terminationHandler的区别

在使用示例中,我们使用waitUntilExit()等待子进程结束,但是waitUntilExit()是同步(阻塞)调用。

try process.run()
process.waitUntilExit()
// 直到进程执行完,这行代码才会继续
print("Process done with code: \(process.terminationStatus)")

因此,可以使用terminationHandler替代waitUntilExit(),terminationHandler是异步回调,在进程结束后回调,在主线程中使用更安全,不会阻塞线程。

let pipe = Pipe()
process.standardOutput = pipe

process.terminationHandler = { process in
    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: data, encoding: .utf8) ?? ""
    print("异步日志:\(output)")
    print("异步退出码:\(process.terminationStatus)")
}

try process.run()
// 立即返回,进程结束时才会调用 terminationHandler

2、terminationStatus的作用

无论使用waitUntilExit() 还是 terminationHandler,都应该使用 process.terminationStatus 来判断进程的退出状态码,因为:

terminationStatus 是系统在子进程退出后设置的属性,告知“这次执行是成功还是失败(或其他状态)”。

1、用 waitUntilExit() 时

try process.run()
process.waitUntilExit()

if process.terminationStatus == 0 {
    print("执行成功")
} else {
    print("执行失败,状态码:\(process.terminationStatus)")
}

因为 waitUntilExit 会确保进程结束,然后才能安全访问 terminationStatus。

2、用 terminationHandler 时

try process.run()
process.terminationHandler = { proc in
    if proc.terminationStatus == 0 {
        print("执行成功")
    } else {
        print("执行失败,状态码:\(proc.terminationStatus)")
    }
}

因为 terminationHandler 只会在进程结束时调用,可以放心使用 terminationStatus。

注意事项:不能在进程还没结束前就访问 terminationStatus,否则:

try process.run()
// 错误做法!这时候 status 还没有正确值
print(process.terminationStatus) // 可能是未定义状态
   

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

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

发表回复

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