简体   繁体   中英

How to run terminal command in swift from any directory?

I'm trying to creating a macOS application that that involves allowing the user to run terminal commands. I am able to run a command, but it runs from a directory inside my app, as far as I can tell. Running pwd returns /Users/<me>/Library/Containers/<My app's bundle identifier>/Data .

How can I chose what directory the command runs from?

I'm also looking for a way to get cd to work, but if I can chose what directory to run the terminal command from, I can handle cd manually.

Here is the code that I'm currently using to run terminal commands:

func shell(_ command: String) -> String {
    let task = Process()
    let pipe = Pipe()

    task.standardOutput = pipe
    task.arguments = ["-c", command]
    task.launchPath = "/bin/zsh"
    task.launch()

    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: data, encoding: .utf8)!

    return output
    
}

I'm using Xcode 12 on Big Sur.

Thanks!

There is a deprecated property currentDirectoryPath on Process .

On the assumption you won't want to use a deprecated property, after reading its documentation head over to the FileManager and look at is provisions for managing the current directory and their implications.

Or just use cd as you've considered – you are launching a shell ( zsh ) with a shell command line as an argument. A command line can contain multiple commands separated by semicolons so you can prepend a cd to your command value.

The latter approach avoids changing your current process' current directory.

HTH

To add to CRD's answer, if using the cd approach, you may also consider separating your commands using && to wait for the previous commands to complete successfully before proceeding to the next command that depends on it.

Try the command you wish to run in the terminal and see if it completes as expected

Eg: /bin/bash -c "cd /source/code/ && git pull && swift build"

If everything works as expected you can go ahead and use it in your swift code as so:
shell("cd /source/code/ && git pull && swift build")


On the topic of deprecations, you may want to replace launchPath with executableURL and launch() with run()

Sample implementation with updated code:

@discardableResult
func shell(_ args: String...) -> Int32 {
    let task = Foundation.Process()
    
    task.executableURL = URL(fileURLWithPath: "/bin/bash")
    task.arguments = ["-c"]
    task.arguments = task.arguments! + args
    
    //Set environment variables
    var environment = ProcessInfo.processInfo.environment
    environment["PATH"]="/usr/bin/swift"
    //environment["CREDENTIALS"] = "/path/to/credentials"
    task.environment = environment
    
    let outputPipe = Pipe()
    let errorPipe = Pipe()
    
    task.standardOutput = outputPipe
    task.standardError = errorPipe
    
    do {
        try task.run()
    } catch {
        // handle errors
        print("Error: \(error.localizedDescription)")
    }
    task.waitUntilExit()
    
    let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
    let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile()
    
    let output = String(decoding: outputData, as: UTF8.self)
    let error = String(decoding: errorData, as: UTF8.self)
    
    //Log or return output as desired
    print(output)
    print("Ran into error while running: \(error)")
    
    return task.terminationStatus
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM