如何使用Swift来实现一个命令行工具的方法

2020-05-15 16:00:18于海丽

本文即简单介绍了如何在Swift中开发命令行工具,以及与Shell命令的交互。水文一篇,不喜勿喷。

主要是使用该工具来解析微信的性能监控组件Matrix的OOM Log。

基本模块

这里,仅简单介绍了常见的基本模块。

Process

Process类可以用来打开另外一个子进程,并监控其运行情况。

    launchPath:指定了执行路径。如可以设置为 /usr/bin/env ,这个命令可以用于打印本机上所有的环境变量;也可以用于执行shell命令,如果你接了参数的话。本文的Demo就用它来执行输入的命令。 arguments:参数,以数组形式传递即可。 launch:调用launch函数即可启动process,用于执行命令。 waitUntilExit:一般执行Shell命令,需要等待命令返回。 terminationStatus:当前process的结束状态,正常为0. standardOutput:standardOutput对应于终端的标准输出。standardError则是错误输出。

Pipe

Pipe这个类就是操作系统的管道,在这里用来接受子进程的输出。这里,可以用于将process的输出传递至管道指定的地方,如一个output变量,或者文件也可以。

fileHandleForReading:pipe从哪里读取内容? fileHandleForWriting:pipe将内容写到哪里?

CommandLine

用于获取脚本参数而已。

print(CommandLine.argc) // 2
print(CommandLine.arguments) // ["./test.swift", "hello"]

封装Shell命令

仅执行Shell命令

这里提供了两种调用Shell命令的封装函数,个人更倾向于第二种,直接将Shell命令及参数封装成一个字符串传入即可。

@discardableResult
func runShell(_ command: String) -> Int32 {
 let task = Process()
 task.launchPath = "/bin/bash"
 task.arguments = ["-c", command]
 task.launch()
 task.waitUntilExit()
 return task.terminationStatus
}

@discardableResult
func runShellWithArgs(_ args: String...) -> Int32 {
 let task = Process()
 task.launchPath = "/usr/bin/env"
 task.arguments = args
 task.launch()
 task.waitUntilExit()
 return task.terminationStatus
}

使用如下:

runShell("pwd")
runShell("ls -l")

runShellWithArgs("pwd")
runShellWithArgs("ls", "-l")

需要Shell命令的输出内容

这里就需要使用到Pipe了。

@discardableResult
func runShellAndOutput(_ command: String) -> (Int32, String?) {
 let task = Process()
 task.launchPath = "/bin/bash"
 task.arguments = ["-c", command]
 
 let pipe = Pipe()
 task.standardOutput = pipe
 task.standardError = pipe
 
 task.launch()
 
 let data = pipe.fileHandleForReading.readDataToEndOfFile()
 let output = String(data: data, encoding: .utf8)
 
 task.waitUntilExit()
 
 return (task.terminationStatus, output)
}

@discardableResult
func runShellWithArgsAndOutput(_ args: String...) -> (Int32, String?) {
 let task = Process()

 task.launchPath = "/usr/bin/env"
 task.arguments = args
 
 let pipe = Pipe()
 task.standardOutput = pipe
 task.standardError = pipe
 
 task.launch()
 
 let data = pipe.fileHandleForReading.readDataToEndOfFile()
 let output = String(data: data, encoding: .utf8)
 
 task.waitUntilExit()
 
 return (task.terminationStatus, output)
}