13
votes

I don't know why this method returns a blank string:

- (NSString *)installedGitLocation {
    NSString *launchPath = @"/usr/bin/which";

    // Set up the task
    NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath:launchPath];
    NSArray *args = [NSArray arrayWithObject:@"git"];
    [task setArguments:args];

    // Set the output pipe.
    NSPipe *outPipe = [[NSPipe alloc] init];
    [task setStandardOutput:outPipe];

    [task launch];

    NSData *data = [[outPipe fileHandleForReading] readDataToEndOfFile];
    NSString *path = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];

    return path;
}

If instead of passing @"git" as the argument, I pass @"which" I get /usr/bin/which returned as expected. So at least the principle works.

from the terminal

$ which which
$ /usr/bin/which
$
$ which git
$ /usr/local/git/bin/git

So it works there.

The only thing I can think of is that which isn't searching through all the paths in my environment.

This is driving me crazy! Does anyone have any ideas?

EDIT: It looks like this is about setting up either NSTask or the user's shell (e.g., ~/.bashrc) so that the correct environment ($PATH) is seen by NSTask.

5
I'm writing a CLI app, and I've found NSTask sees the $PATH when you run your program from the CLI. Unfortunately, you must compile and run on the command line as well, because testing the program in Xcode brings us to this situation.ray

5 Answers

23
votes

Try,

    [task setLaunchPath:@"/bin/bash"];
    NSArray *args = [NSArray arrayWithObjects:@"-l",
                     @"-c",
                     @"which git",
                     nil];
    [task setArguments: args];

This worked for me on Snow Leopard; I haven't tested on any other system. The -l (lowercase L) tells bash to "act as if it had been invoked as a login shell", and in the process it picked up my normal $PATH. This did not work for me if the launch path was set to /bit/sh, even with -l.

13
votes

Running a task via NSTask uses fork() and exec() to actually run the task. The user's interactive shell isn't involved at all. Since $PATH is (by and large) a shell concept, it doesn't apply when you're talking about running processes in some other fashion.

2
votes

Is /usr/local/git/bin in your $PATH when you run the program? I think which only looks in the user's $PATH.

1
votes

Take a look at the question Find out location of an executable file in Cocoa. It looks like the basic problem is the same. The answer unfortunately isn't nice and neat, but there's some useful info there.

1
votes

In Swift NSTask is replaced by Process but this is what it works for me:

let process = Process()
process.launchPath = "/bin/bash"
process.arguments = ["-l", "-c", "which git"]