6

I am new toSswift. How can I run this process from Swift code?

  1. open Terminal window
  2. execute cd Desktop/firebase-mac
  3. execute npm start

What I am actually trying to do is to start Node server on click from Swift code.

2
  • I have tried task.launchPath = "/usr/bin/env" task.arguments = ["/Users/aharon/Desktop"] and i get Permission denied Commented Mar 25, 2017 at 9:23
  • There are various reasons to want to run commands in the Terminal, but opening a terminal from a program to do that is almost always the wrong thing. Let the user run the program in a terminal if they want to. If you are unfamiliar with the shell, just take my word for it; you usually don't need the terminal to run a program, and when you do, that should be the invoking user's choice. Commented Jul 3, 2020 at 8:14

4 Answers 4

14

Full examples:

  • go to some directory, let say Desktop
  • Create a file with name swsh, and add into it (plaintext, not rtf, or doc)
#!/usr/bin/env xcrun swift

import Foundation

func shell(launchPath: String, arguments: [String]) -> String {

    let process = Process()
    process.launchPath = launchPath
    process.arguments = arguments

    let pipe = Pipe()
    process.standardOutput = pipe
    process.launch()

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

    // remove the trailing new-line char
    if output_from_command.characters.count > 0 {
        let lastIndex = output_from_command.index(before: output_from_command.endIndex)
        return output_from_command[output_from_command.startIndex ..< lastIndex]
    }
    return output_from_command
}

let output = shell(launchPath: "/bin/date", arguments: [ ])
print(output)

Save, and:

  • open the Terminal
  • type cd ~/Desktop
  • use chmod 755 swsh
  • and run your swift script as: ./swsh

You will get output like:

Sat Mar 25 14:31:39 CET 2017

Edit your swsh and change the shell(... line to:

let output = shell(launchPath: "/usr/bin/env", arguments: [ "date" ])

run it and again will get the date, but now:

  • the swsh executed the /usr/bin/env, (with the argument date)
  • and the /usr/bin/env finds the command date
  • and executed it

Now, create another file in the ~/Desktop and name it as from_swift.

Add into it

echo "Today's date is $(date)"

change the file swsh change the shell line to:

let output = shell(launchPath: "./from_swift", arguments: [ ])

Note, the ./from_swift - using relative path to . (we are in the ~/Desktop directory). Run the swift program:

./swsh

Output:

2017-03-25 14:42:20.176 swift[48479:638098] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'launch path not accessible'

Of course, the script from_swift is not executable yet. So execute:

chmod 755 from_swift
# and run
./swsh

Again error:

2017-03-25 14:45:38.523 swift[48520:639486] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Couldn't posix_spawn: error 8'

This is because the from_swift is a script (not a compiled binary), so the operating system need to know which binary should interpret the script content. Because this is an shell script edit the from_swift script as:

#!/bin/sh
echo "Today's date is $(date)"

Note the added "shebang" line: #!/bin/sh. Run the swift ./swsh and will get

Today's date is Sat Mar 25 14:50:23 CET 2017

Horray, you executed your 1st bash script from swift. ;)

Of course, you can use the /usr/bin/env in the shebang, so now change, the content of the from_swift for example to:

#!/usr/bin/env perl

use strict;
use utf8;
use English;
binmode STDOUT, ":utf8";

printf "The $EXECUTABLE_NAME (ver:$PERL_VERSION) runs me: $0\n";
printf "I ❤️ perl!\n";

Run the ./swsh and will get:

The /usr/bin/perl (ver:v5.18.2) runs me: ./from_swift
I ❤️ perl!

NOTE, we changed nothing in the ./swsh file, just the script file ./from_swift!

All the above done using:

$ uname -a
Darwin nox.local 16.4.0 Darwin Kernel Version 16.4.0: Thu Dec 22 22:53:21 PST 2016; root:xnu-3789.41.3~3/RELEASE_X86_64 x86_64
$ swift --version
Apple Swift version 3.0.2 (swiftlang-800.0.63 clang-800.0.42.1)
Target: x86_64-apple-macosx10.9

So, it is easy to create and execute any script. So, you can enter into your ~/Desktop/from_swift

#!/bin/sh
cd $HOME/Desktop/firebase-mac
npm start

It is possible to do directly from the swsh, (Jens Meder proposed), but using this you got very easy method executing anything from the given script file.

Just remember: the process.launch() executes either:

  • compiled binaries
  • or script files, but the script files
    • must have the shebang line
    • and must be executable using chmod 755 /path/to/script.file
Sign up to request clarification or add additional context in comments.

Comments

3

You can just use the following code snippet, e.g., run("npm start"). It will execute the command on the default shell /bin/sh and return the output as a String.

func run(_ cmd: String) -> String? {
    let pipe = Pipe()
    let process = Process()
    process.launchPath = "/bin/sh"
    process.arguments = ["-c", String(format:"%@", cmd)]
    process.standardOutput = pipe
    let fileHandle = pipe.fileHandleForReading
    process.launch()
    return String(data: fileHandle.readDataToEndOfFile(), encoding: .utf8)
}

If you want to use a different bash then just replace the launchPath, e.g., for Zsh it would be /bin/zsh.

7 Comments

for "/bin/sh" i get "command not found" for "/bin/env" i get "launch path not accessible"
Could you try /bin/bash, please?
Could you run echo $SHELL in a new Terminal window and try that output? For me it outputs /bin/zsh because I use Zsh as shell.
the result is /bin/bash
I create a shell script #!/bin/bash cd /users/aharon/desktop/firebase-mac npm start It works fine when i run it , But if i run it from the code i get line 3: npm: command not found
|
1

using swift-commands

import Commands

Commands.Bash.system("cd /Users/${name}/Desktop/firebase-mac && npm start")

Comments

0

To launch a process passing one argument you simply need one line:

Process.launchedProcess(launchPath: "/Users/aharon/Desktop/firebase-mac/npm", arguments: ["start"])

But consider that the literal path does not work if the application is sandboxed.

3 Comments

I create a shell script #!/bin/bash cd /users/aharon/desktop/firebase-mac npm start It works fine when i run it , But if i run it from the code i get line 3: npm: command not found
Have you tried my code as it is? Forget the extra cd command. You don't need it. Just pass the full path /users/aharon/desktop/firebase-mac/npm start
i get "launch path not accessible"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.