 
 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 task = makeTask(for: command, with: arguments, environmentVariables: environmentVariables)
 
         let pipe = Pipe()
         task.standardOutput = pipe

                                       task: task)
    }

    private func mergeEnvs(localEnv: [String: String], processEnv: [String: String]) -> [String: String] {
        return localEnv.merging(processEnv, uniquingKeysWith: { (_, envString) -> String in
            envString


    // 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
