Swift - Execute Shell command with waiting for execution

Good morning,

in my OSx App I would like to execute a command from the shell. As long as I did it with apple script or in the Terminal window it worked fine. but when I execute it via swift it won't work. the command is:


ssh -p 10001 root@nozbfa.anywareip.net -L 23389:192.168.38.11:3389 -f sleep 20


I tried it via a function


@discardableResult 
func shell(_ args: String...) -> String { 
let task = Process() 
task.launchPath = "/bin/bash/" 
task.arguments = args 
let pipe = Pipe() 
task.standardOutput = pipe 
task.launch() 
task.waitUntilExit() 
let data = pipe.fileHandleForReading.readDataToEndOfFile() 
guard let output: String = String(data: data, encoding: .utf8) else { return "" } return output 
}


shell("ssh", script)


var script is:


-p 10001 root@nozbfa.anywareip.net -L 23389:192.168.38.11:3389 -f sleep 20


and it returns:


/usr/bin/ssh: /usr/bin/ssh: cannot execute binary file


what am I doing wrong?

Accepted Reply

I would like to execute a command from the shell

I strongly recommend against that. The shell is a vastly complex system with lots of sharp edges, which makes it much harder to call via

Process
. For example, every time you want to pass in a path, you have to quote that path. Worse yet, this complexity often introduces really subtle security vulnerabilities.

It’s better to invoke the command in question (in this case

ssh
) directly.

Having said that, I can certainly offer you feedback about your code:

  • Line 4 is wrong. You shouldn’t rely on

    bash
    being present, and you certainly shouldn’t have a trailing slash on it. Use
    /bin/sh
    .
  • I don’t understand what you’re trying to do with line 5. Generally the shell takes:

    • A path to a script as an argument

    • A script via the

      -c
      option

    It looks like you’re trying to pass in the script without using

    -c
    , which is not going to work.
  • Lines 9 and 10 need to be reversed. Remember that pipes have a limited buffer size (can’t find the value right now but it’s around 8 KiB if I remember correctly). If the child process outputs more than that amount of data before exiting, it’s write will block. That’ll deadlock because:

    • You don’t start reading until the child exits

    • The child won’t exit until you start reading

Finally, is your app sandboxed? If so, that introduces another level of complexity (-:

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

I would like to execute a command from the shell

I strongly recommend against that. The shell is a vastly complex system with lots of sharp edges, which makes it much harder to call via

Process
. For example, every time you want to pass in a path, you have to quote that path. Worse yet, this complexity often introduces really subtle security vulnerabilities.

It’s better to invoke the command in question (in this case

ssh
) directly.

Having said that, I can certainly offer you feedback about your code:

  • Line 4 is wrong. You shouldn’t rely on

    bash
    being present, and you certainly shouldn’t have a trailing slash on it. Use
    /bin/sh
    .
  • I don’t understand what you’re trying to do with line 5. Generally the shell takes:

    • A path to a script as an argument

    • A script via the

      -c
      option

    It looks like you’re trying to pass in the script without using

    -c
    , which is not going to work.
  • Lines 9 and 10 need to be reversed. Remember that pipes have a limited buffer size (can’t find the value right now but it’s around 8 KiB if I remember correctly). If the child process outputs more than that amount of data before exiting, it’s write will block. That’ll deadlock because:

    • You don’t start reading until the child exits

    • The child won’t exit until you start reading

Finally, is your app sandboxed? If so, that introduces another level of complexity (-:

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"