如果您希望像在命令行中一样“精确”使用命令行参数(而不是分离所有参数),请尝试以下方法。
(此答案改进了LegoLess的答案,可用于Swift 5)
import Foundation
func shell(_ command: String) -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.arguments = ["-c", command]
task.launchPath = "/bin/zsh"
task.standardInput = nil
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
shell("ls -la")
已更新/更安全的函数调用 10/23/21: 可能会在上述shell命令中遇到运行时错误,如果是这样,请尝试切换到下面更新的调用。您需要在新的shell命令周围使用do catch语句,但希望这能为您节省一些搜索捕获意外错误的时间。
解释: 由于task.launch()不是一个可抛出的函数,因此无法被捕获,我发现有时仅仅调用它就会导致应用程序崩溃。经过大量的互联网搜索,我发现Process类已经将task.launch()废弃,转而采用较新的函数task.run(),该函数可以正确地抛出错误而不会使应用程序崩溃。要了解有关更新方法的更多信息,请参见:
https://eclecticlight.co/2019/02/02/scripting-in-swift-process-deprecations/
import Foundation
@discardableResult
func safeShell(_ command: String) throws -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.arguments = ["-c", command]
task.executableURL = URL(fileURLWithPath: "/bin/zsh")
task.standardInput = nil
try task.run()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
示例:
do {
try safeShell("ls -la")
}
catch {
print("\(error)")
}
let result = try? safeShell("ls -la")
try? safeShell("ls -la")
注意:针对最后一种情况,您正在使用
try?
并且没有使用结果,由于某种原因,即使它被标记为
@discardableResult
,编译器仍然会发出警告。 这只发生在
try?
中,而不是在
do-try-catch
块内或从抛出函数中。 无论哪种方式,您都可以安全地忽略它。
/bin/bash
指的是bash-3.2
。如果你想使用bash的更高级功能,请更改路径(通常/usr/bin/env bash
是一个不错的选择)。 - Aserrethrows
,你不需要使用do..try..catch块,只需使用try task.run()
即可。 - Przemysław Wrzesińskitask.run()
之前,您还应添加task.standardInput = nil
。如果子进程尝试从stdin
读取数据,内核将_默默停止_子进程,这可能会非常难以诊断。这可能很微妙,因为子进程可以在_监视_stdin
的同时执行有用的工作。当您在键盘上输入内容时,子进程会看到有等待的输入并调用read()
。内核通过发送SIGSTOP
来响应,并使子进程冻结。我很惊讶这不是Process()
的默认设置。 - Jim Hayes