Skip to content

Instantly share code, notes, and snippets.

@jaekong
Last active September 19, 2024 12:18
Show Gist options
  • Save jaekong/fe217aaaad283cd74c115772dfc3958c to your computer and use it in GitHub Desktop.
Save jaekong/fe217aaaad283cd74c115772dfc3958c to your computer and use it in GitHub Desktop.
cli.swift - A collection of small snippets for easy Swift command-line scripting
#!/usr/bin/env swift
import Foundation
@discardableResult
func run(_ command: String, shell: String = "zsh", passthrough: Bool = false, stdInContent: String? = nil) throws -> String {
let process = Process()
let stdOut = Pipe()
let stdErr = Pipe()
process.standardOutput = stdOut
process.standardError = stdErr
if let stdInContent, let data = stdInContent.data(using: .utf8) {
let stdIn = Pipe()
try stdIn.fileHandleForWriting.write(contentsOf: data)
process.standardInput = stdIn
}
process.standardInput = nil
let arguments = [shell, "-c", command]
process.executableURL = URL(filePath: "/usr/bin/env")
process.arguments = arguments
try process.run()
if let stdErrData = try stdErr.fileHandleForReading.readToEnd() {
guard let string = String(data: stdErrData, encoding: .utf8) else {
throw "STDERR Parsing Error"
}
throw string
}
guard let data = try stdOut.fileHandleForReading.readToEnd() else {
return ""
}
guard let string = String(data: data, encoding: .utf8) else {
throw "STDOUT Parsing Error"
}
if passthrough {
print(string)
}
return string
}
prefix operator !
prefix operator !!
prefix operator >
prefix operator ?>
@discardableResult
prefix func !(_ command: String) -> String {
return try! run(command, passthrough: false)
}
@discardableResult
prefix func !!(_ command: String) -> String {
return try! run(command, passthrough: true)
}
@discardableResult
prefix func >(_ command: String) throws -> String {
return try run(command, passthrough: false)
}
@discardableResult
prefix func ?>(_ command: String) -> String? {
guard let result = try? run(command, passthrough: false) else {
return nil
}
return result
}
extension String: Error {
var localizedDescription: String { return self }
}
func printInfo(_ content: Any) {
print(content, to: &FileHandle.standardError)
}
extension FileHandle: TextOutputStream {
public func write(_ string: String) {
guard let data = string.data(using: .utf8) else { return }
self.write(data)
}
}

cli.swift

It's a small collection of functions to make scripting in Swift easier. (with #!/usr/bin/env swift or with swift-sh)

All of these were written kinda try!-ish while I was scripting casually, so I might fix it later.

printInfo(_ content: Any)

Pass content to Swift.print() function with to: &FileHandle.standardError option.

To make this work, FileHandle type is extended to conform to TextOutputStream.

run(_ command: String, shell: String = "zsh", passthrough: Bool = false, stdInContent: String? = nil) throws -> String

Run a shell command.

  • shell determines what shell environment it should run in.
  • passthrough determines if stdout of child process should also be printed out to this process.
  • stdInContent gets piped in as standard input for the child process.

throws stderr content as an error if the command outputs to stderr.

To make this work, String type is extended to conform to Error.

I should make it not an error for the child process to print something in stderr.

Rather, I should make it throw when the process exits with non-zero code.

!"command"

Run "command" and exit if the shell command fails.

!!"command"

Run "command" and exit if the shell command fails. Passthrough is turned on, so the main process shows the stdout of the child process.

>"command" throws

Run "command" and throw if the shell command fails.

?>"command"

Run "command" and if the shell command fails, return nil.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment