The
which
command returns its result by printing
stdout
. The
run
method doesn’t return that; it actually returns nothing (
Void
) because the process executes asynchronously. To collect the output you have to do two things:
Doing this correctly is tricky for obscure low-level reasons. Pasted in below is some code I had lying around for this. If I run it like so:
try! launch(tool: URL(fileURLWithPath: "/usr/bin/which"), arguments: ["sips"]) { (status, outputData) in
let output = String(data: outputData, encoding: .utf8) ?? ""
print("done, status: \(status), output: \(output)")
}
it prints:
done, status: 0, output: /usr/bin/sips
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"
func launch(tool: URL, arguments: [String], completionHandler: @escaping (Int32, Data) -> Void) throws {
let group = DispatchGroup()
let pipe = Pipe()
var standardOutData = Data()
group.enter()
let proc = Process()
proc.executableURL = tool
proc.arguments = arguments
proc.standardOutput = pipe.fileHandleForWriting
proc.terminationHandler = { _ in
proc.terminationHandler = nil
group.leave()
}
group.enter()
DispatchQueue.global().async {
// Doing long-running synchronous I/O on a global concurrent queue block
// is less than ideal, but I’ve convinced myself that it’s acceptable
// given the target ‘market’ for this code.
let data = pipe.fileHandleForReading.readDataToEndOfFile()
pipe.fileHandleForReading.closeFile()
DispatchQueue.main.async {
standardOutData = data
group.leave()
}
}
group.notify(queue: .main) {
completionHandler(proc.terminationStatus, standardOutData)
}
try proc.run()
// We have to close our reference to the write side of the pipe so that the
// termination of the child process triggers EOF on the read side.
pipe.fileHandleForWriting.closeFile()
}