Search code examples
cocoaunixpipenstask

NSTask Does Not Terminate


I'm trying to use NSTask to run the UNIX 'apropos' command. Here's my code:

NSTask *apropos = [[NSTask alloc] init];
NSPipe *pipe = [[NSPipe alloc] init];
[apropos setLaunchPath:@"/usr/bin/apropos"];
[apropos setArguments:[NSArray arrayWithObjects:@"filename", @"match", nil]];
[apropos setStandardOutput:pipe];
[apropos launch];
[apropos waitUntilExit];

The problem is that this never returns. I also tried using Apple's example code (TaskWrapper) and it returns the output (in three segments) but it never calls the processFinished handler.

Furthermore, the appendOutput: handler receives duplicates. So, for example, if apropos returns this:

1 2 3 4 5

I might receive something like this:

1 2 3

1 2 3 4

5

(grouped into 3 append messages).

I note that Apropos displays the output in a format where it's possible to scroll up and down in the command line instead of just directly outputting the data straight to standard output; how do I read this reliably through NSTask and NSPipe?


Solution

  • I’ve just tested this program and it works fine: the program terminates and /tmp/apropos.txt contains the output of apropos.

    #import <Foundation/Foundation.h>
    
    int main()
    {
        NSAutoreleasePool *pool = [NSAutoreleasePool new];
    
        NSTask *apropos = [[[NSTask alloc] init] autorelease];
        NSPipe *pipe = [[[NSPipe alloc] init] autorelease];
        NSFileHandle *readHandle = [pipe fileHandleForReading];
        [apropos setLaunchPath:@"/usr/bin/apropos"];
        [apropos setArguments:[NSArray arrayWithObjects:@"filename", @"match", nil]];
        [apropos setStandardOutput:pipe];
        [apropos launch];
        [apropos waitUntilExit];
    
        NSString *output = [[[NSString alloc]
            initWithData:[readHandle readDataToEndOfFile]
                encoding:NSUTF8StringEncoding] autorelease];
    
        [output writeToFile:@"/tmp/apropos.txt" atomically:YES
            encoding:NSUTF8StringEncoding error:NULL];
    
        [pool drain];
        return 0;
    }
    

    Are you by any chance using NSLog() to inspect the output? If so, you might need to set a pipe for stdin as explained in this answer of mine to an NSTask related question. It seems that NSLog() sending data to stderr affects NSTask.