Skip to content

Instantly share code, notes, and snippets.

@AlexKobachiJP
Created February 21, 2023 08:55
Show Gist options
  • Save AlexKobachiJP/20602d3d99b600fb6854f7d9c3bdd1e6 to your computer and use it in GitHub Desktop.
Save AlexKobachiJP/20602d3d99b600fb6854f7d9c3bdd1e6 to your computer and use it in GitHub Desktop.
Creates a process and launches an exectuable with arguments.
// Copyright © 2022 Alex Kovács. All rights reserved.
#if os(macOS)
import Foundation
extension Process {
/// Creates a process and launches an exectuable with arguments.
///
/// Optionally pass environment variables for the process.
/// - Parameters:
/// - launchPath: Absolute path to an exectuable. E.g., `/usr/bin/git` (not `git`).
/// - arguments: Array of command line arguments to pass to the executable. E.g., `["clone", "[email protected]:EbookFoundation/free-programming-books.git", "/Users/alex/Desktop"]`.
/// - environment: Dictionary of environment variables and their values.
/// - routeStdErrToStdOut: If `true`, `stderr` will be routed to `stdout` (same as `2>&1` on the command line).
/// - Returns: Output to `stdout`, output to `stderr`, and the termination status code of the executable.
public static func run(_ launchPath: String, _ arguments: [String] = [], _ environment: [String: String]? = nil, routeStdErrToStdOut: Bool = false) throws -> ProcessExecutionResult {
let task = Process()
task.executableURL = URL(filePath: launchPath)
task.arguments = arguments
task.environment = environment
let standardOutputPipe = Pipe()
task.standardOutput = standardOutputPipe
let standardErrorPipe = routeStdErrToStdOut ? standardOutputPipe : Pipe()
task.standardError = standardErrorPipe
try task.run()
task.waitUntilExit()
return .init(
output: try standardOutputPipe.fileHandleForReading.readToEnd().map { String(data: $0, encoding: .utf8)! },
error: try standardErrorPipe.fileHandleForReading.readToEnd().map { String(data: $0, encoding: .utf8)! },
status: Int(task.terminationStatus)
)
}
/// Same as ``run(_:_:_:routeStdErrToStdOut:input:)`` but defaulting the `launchPath` to `/bin/bash`.
///
/// For example:
/// ```
/// let result = try Process.runScript(arguments: ["Hello, Argument!"]) {"""
/// echo $0
/// """}
/// ```
/// - Parameters:
/// - switch: The switch for the shell to take input via a string. Defaults to `"-c"` which is the option of Bash to read commands from the following string.
/// - script: A closure returning a script string.
public static func runScript(_ launchPath: String = "/bin/bash", _ switch: String = "-c", arguments: [String] = [], _ environment: [String: String]? = nil, routeStdErrToStdOut: Bool = false, script: () -> String) throws -> ProcessExecutionResult {
let arguments = [`switch`, script()] + arguments
return try run(launchPath, arguments, environment, routeStdErrToStdOut: routeStdErrToStdOut)
}
}
/// The result of executing a process.
public struct ProcessExecutionResult {
/// The output to standard output of the executable.
var output: String?
/// The output to standard error of the executable.
var error: String?
/// The termination status code of the executable.
var status: Int
}
extension ProcessExecutionResult: Equatable {}
extension ProcessExecutionResult: Hashable {}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment