Last active
September 11, 2024 06:46
-
-
Save andreacipriani/8c3af3719da31c8fae2cdfa8c21e17ba to your computer and use it in GitHub Desktop.
Execute shell/bash commands from Swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
protocol CommandExecuting { | |
func run(commandName: String, arguments: [String]) throws -> String | |
} | |
enum BashError: Error { | |
case commandNotFound(name: String) | |
} | |
struct Bash: CommandExecuting { | |
func run(commandName: String, arguments: [String] = []) throws -> String { | |
return try run(resolve(commandName), with: arguments) | |
} | |
private func resolve(_ command: String) throws -> String { | |
guard var bashCommand = try? run("/bin/bash" , with: ["-l", "-c", "which \(command)"]) else { | |
throw BashError.commandNotFound(name: command) | |
} | |
bashCommand = bashCommand.trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines) | |
return bashCommand | |
} | |
private func run(_ command: String, with arguments: [String] = []) throws -> String { | |
let process = Process() | |
process.launchPath = command | |
process.arguments = arguments | |
let outputPipe = Pipe() | |
process.standardOutput = outputPipe | |
process.launch() | |
let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile() | |
let output = String(decoding: outputData, as: UTF8.self) | |
return output | |
} | |
} | |
let bash: CommandExecuting = Bash() | |
if let lsOutput = try? bash.run(commandName: "ls", arguments: []) { print(lsOutput) } | |
if let lsWithArgumentsOutput = try? bash.run(commandName: "ls", arguments: ["-la"]) { print(lsWithArgumentsOutput) } |
This works great, thank you for the gist!
I do get an error when using the command, although the given command is still executed. I get /bin/bash: /etc/profile: Operation not permitted
before every print out of my original command.
Example code:
let cli = Bash()
if let output = try? cli.run(commandName: "pwd") {
print(output)
}
I did change the import from UiKit
to AppKit
since I'm developing for MacOS.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Also, perhaps it would be beneficial to factor out
which
apart fromrun
to allow a consumer to decide what exactly to execute:: https://gist.github.com/gary17/b6c8b662da36cefee7010cd4bdcb2ee6