0

I have a cocoa app and I want to launch a shell script that launches Node.js. I figured I would do that with NSTask

NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/bin/bash/start.sh"];
[task setArguments:[NSArray arrayWithObjects:@"start.sh", nil]];
[task setStandardOutput:[NSPipe pipe]];
[task setStandardInput:[NSPipe pipe]];

[task launch];

The script is in the root of my application. I have tried many variations in the launch path and I am stuck. Any help would be greatly appreciated!

Edit:

Here is my new code where i set the argument. It still will not work for some reason.

NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/bin/bash"];
[task setArguments:[NSArray arrayWithObjects:[[NSBundle mainBundle] pathForResource:@"start" ofType:@"sh"], nil]];
[task launch];
Mina Dawoud
  • 408
  • 8
  • 22

3 Answers3

2

As you guessed, the problem is your launch path.

The launch path must be a single path to a file. Filenames won't work*, and you can't name two separate files in the same path.

Your launch path should be the complete absolute path to bash. Use which bash in the Terminal to find out which one that'll be. (It should be /bin/bash on a stock OS X install.)

To tell bash to run the script, you need to identify it in the arguments. Just saying the script's name won't cut it; you must give the script's complete absolute path. There's no reason to have the script's filename alone anywhere.

I assume the script is a resource in your application bundle. To get its absolute path, ask your main bundle for the URL to that resource, and then get that URL's path.

(You can ask for a path for a resource directly, but working with URLs is a habit worth developing. If you need a concrete advantage, the URL-based method correctly calls its second argument a filename extension, not a “type”; “type” means something different in modern usage.)


*Filenames are treated as any other relative paths. Relative paths can work, but need to resolve to a path that exists relative to the current working directory. By default, that's / (the root directory), so any relative path that would work is functionally equivalent to an absolute path. You should just use absolute paths everywhere.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • I edited my answer where i set the argument and change path. It still will not work. – Mina Dawoud Oct 04 '13 at 21:16
  • pathForResource: is where I think it is wrong. The documentation is confusing me on how to obtain the correct path. – Mina Dawoud Oct 04 '13 at 21:39
  • 1
    @MinaDawoud: In what way? – Peter Hosey Oct 04 '13 at 21:51
  • It talks about NSBundles and NSPaths, but it does not link it back to the NSTask. – Mina Dawoud Oct 04 '13 at 21:52
  • 1
    @MinaDawoud: There is no NSPath; paths are NSStrings. And it doesn't refer to NSTask because getting a path is (or at least was) not an NSTask-specific… well, task. `pathForResource:ofType:` or `URLForResource:withExtension:` *is* how you get the path to a resource in your bundle; that path is suitable for almost all immediate uses, including passing to a task using NSTask. – Peter Hosey Oct 04 '13 at 22:04
  • Can you point me where in my edited code I need to focus on. I am using pathForResource:ofType: but it is still not working. – Mina Dawoud Oct 04 '13 at 22:05
  • @MinaDawoud: What do you mean by “not working”? You seem to have removed the messages that set its standard input and output, so those won't work anymore if you're trying to use those later. If you mean it can't find the script—well, I assumed that you have the script as a resource in your app bundle. Your revised code is correct for that case. If it's not working, that means that the script is not in the app bundle. Either my assumption was wrong or you haven't added the script to the project and/or the target. – Peter Hosey Oct 04 '13 at 22:08
1

you should try this:

NSString *path = [[NSBundle mainBundle] pathForResource:@"SCRIPT_NAME" ofType:@"sh"];
NSString *commandToRun =[NSString stringWithFormat:@"/usr/bin/osascript -e\
                         'do shell script \"%@ args 2>&1 etc\" with administrator\
                         privileges'",path];
NSArray *arguments = [NSArray arrayWithObjects:
                      @"-c" ,
                      [NSString stringWithFormat:@"%@", commandToRun],
                      nil];
[task setLaunchPath:@"/bin/sh"];
[task setArguments:arguments];

[task launch];
[task waitUntilExit];               // wait for completion
if ([task terminationStatus] != 0)  // check termination status
{
    NSLog(@"termination status is %d",[task terminationStatus]);

}

if the terminationStatus != 0 , there is a problem with task. something wrong, you should check the script.

Almog_0
  • 422
  • 4
  • 11
0

I had this exact same problem, and this is how I solved it. Assuming you are using a bundle application, you can just ask NSBundle for the path to the application executable. If your script is in the same directory as the executable (<name>.app/Contents/MacOS/), then this should work.

NSString *wd = [[NSBundle mainBundle] executablePath];
// this creates a path to <name.app>/Contents/MacOS/<name>/../script, where <name>
//    is the name of your app's executable file
NSString *path = @"/../script";
NSString *fullPath = [wd stringByAppendingString: path ];

[scriptTask setLaunchPath: fullPath];
[scriptTask launch];
Haz
  • 2,539
  • 1
  • 18
  • 20