For Swift v.5+:
Does not contain any "deprecated" phrases at the time of writing. Does not crash if an unknown command is called.
extension Process { static func shell(path: String = "/bin/zsh", args:[String] = []) -> (Int32, String?, String?) { let task = Process() let pipeOut = Pipe() let pipeErr = Pipe() task.standardInput = nil task.standardOutput = pipeOut task.standardError = pipeErr task.executableURL = URL(fileURLWithPath: path) task.arguments = args do { try task.run() task.waitUntilExit() let dataOut = pipeOut.fileHandleForReading.readDataToEndOfFile() let dataErr = pipeErr.fileHandleForReading.readDataToEndOfFile() return ( task.terminationStatus, dataOut.isEmpty ? nil : String(data: dataOut, encoding: .utf8), dataErr.isEmpty ? nil : String(data: dataErr, encoding: .utf8) ) } catch { return (0, nil, nil) } } }
Example for call NodeJS script:
do { let (status, output, error) = Process.shell( path: "/usr/local/bin/node", args: ["./script.js"] ) dump(status) dump(output) dump(error) }
Example for call Python script:
do { let (status, output, error) = Process.shell( path: "/usr/local/bin/python3", args: ["./script.py"] ) dump(status) dump(output) dump(error) }
Example for call PHP script:
do { let (status, output, error) = Process.shell( path: "/usr/local/bin/php", args: ["./script.php"] ) dump(status) dump(output) dump(error) }
Example for call Bash command:
do { let (status, output, error) = Process.shell( path: "/bin/bash", args: ["-c", "command"] ) dump(status) dump(output) dump(error) } do { let (status, output, error) = Process.shell( path: "/usr/bin/env", args: ["bash", "-c", "command"] ) dump(status) dump(output) dump(error) }
Research of all use cases
All of the following options work fine directly via Bash, but calling them via Swift results in the following:
Shell Command (bash → command):
command /bin/bash -c command /usr/bin/env bash -c command
Call script via Bash (bash → script|shebang(interpreter)|chmod+x):
./script -> PY: import fail | PHP: No such file | JS: No such file /bin/bash -c ./script -> PY: import fail | PHP: No such file | JS: No such file /usr/bin/env bash -c ./script -> PY: import fail | PHP: No such file | JS: No such file
Call script directly (interpreter → script):
interpreter ./script -> PY: nil | PHP: nil | JS: nil /usr/local/bin/interpreter ./script -> PY: OK | PHP: OK | JS: OK /usr/bin/env interpreter ./script -> PY: import fail | PHP: No such file | JS: No such file