5

I am making an OS X App that requires running an shell script. Here are my swift code:

func runTask(arguments: [String]) {
    output.string = ""

    task = NSTask()
    task.launchPath = "/bin/bash"
    task.arguments = arguments;

    errorPipe = NSPipe()
    outputPipe = NSPipe()

    task.standardError = errorPipe
    task.standardOutput = outputPipe

    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(didCompleteReadingFileHandle(_:)), name: NSFileHandleReadCompletionNotification, object: task.standardOutput!.fileHandleForReading)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(didCompleteReadingFileHandle(_:)), name: NSFileHandleReadCompletionNotification, object: task.standardError!.fileHandleForReading)

    errorPipe.fileHandleForReading.readInBackgroundAndNotify()
    outputPipe.fileHandleForReading.readInBackgroundAndNotify()

    task.launch()
}

func didCompleteReadingFileHandle(sender: NSNotification) {
    let data: NSData = sender.userInfo![NSFileHandleNotificationDataItem] as! NSData;
    let string = NSString(data: data, encoding: NSUTF8StringEncoding)!
    // The output property is a NSTextView object
    output.string?.appendContentsOf(String(string))
}

Now I tried calling the runTask method:

runTask(["/bin/echo", "1234"])

It says the following error:

/bin/echo: /bin/echo: cannot execute binary file

Now I went back into Terminal and typed in echo 1234 it runs perfectly without any trouble, now how do you get this to work? Thanks.

Tom Shen
  • 1,838
  • 3
  • 19
  • 40

2 Answers2

14

bash has three main modes of operation:

  1. If you pass it -c "some command string", it'll execute that command string.

  2. If you pass it a file path as an argument, it'll read commands from that file and execute them (i.e. execute the file as a shell script).

  3. If you don't pass it any arguments, it'll read and execute commands from standard input.

Since you passed it the arguments "/bin/echo" and "1234", it's assuming you want mode 2, so it tries to read shell commands from /bin/echo, and fails. I'm not clear on exactly what you're trying to achieve, but I see several options that might be relevant:

  • If you're trying to execute a binary (e.g. /bin/echo), just execute that directly without using bash at all:

    task.launchPath = "/bin/echo"
    task.arguments = ["1234"]
    
  • If you need to execute a command string (i.e. if you need the shell to parse it before executing it, so e.g. wildcards get expanded, or there's more than one command, or...), use bash -c:

    task.launchPath = "/bin/bash"
    task.arguments = ["-c", "/bin/echo 1234; ls *"]
    
  • If you need to execute an actual script, i.e. a file with shell commands in it, then leave runTask alone, but pass it an actual script:

    runTask(["/path/to/script", "scriptarg", "another argument"])
    
Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
1

You're executing /bin/bash /bin/echo which doesn't work in Terminal.app either.

Remove /bin/bash

task.launchPath = "/bin/echo"
...
runTask(["1234"])
vadian
  • 274,689
  • 30
  • 353
  • 361