                                        task: task)
     }
 
+    private func makeTask(for command: String, with arguments: [String], environmentVariables: [String: String]) -> Process {
+        let script = [command,
+                      arguments.joined(separator: " ")].filter { !$0.isEmpty }.joined(separator: " ")
+        let processEnv = ProcessInfo.processInfo.environment
+        let task = Process()
+        task.launchPath = processEnv["SHELL"]
+        task.arguments = ["-l", "-c", script]
+        task.environment = mergeEnvs(localEnv: environmentVariables, processEnv: processEnv)
+        task.currentDirectoryPath = FileManager.default.currentDirectoryPath
+        return task
+    }
+
     private func mergeEnvs(localEnv: [String: String], processEnv: [String: String]) -> [String: String] {
         return localEnv.merging(processEnv, uniquingKeysWith: { (_, envString) -> String in
             envString


internal class ShellExecutor {
    func execute(_ command: String, arguments: [String] = [], environmentVariables: [String: String] = [:]) -> String {
        let script = [command,
                      arguments.joined(separator: " ")].filter { !$0.isEmpty }.joined(separator: " ")
        print("Executing `\(script)` with environment variables \(environmentVariables)")

        var env = ProcessInfo.processInfo.environment
        let task = Process()
        task.launchPath = env["SHELL"]
        task.arguments = ["-l", "-c", script]
        task.environment = mergeEnvs(localEnv: environmentVariables, processEnv: env)
        task.currentDirectoryPath = FileManager.default.currentDirectoryPath

        let pipe = Pipe()
        task.standardOutput = pipe


    // Similar to above, but can throw, and throws with most of
    // what you'd probably need in a scripting environment
    func spawn(_ command: String, arguments: [String] = [], environmentVariables: [String: String] = [:]) throws -> String {
        let task = makeTask(for: command, with: arguments, environmentVariables: environmentVariables)

        let stdout = Pipe()
        task.standardOutput = stdout
