1

I'd like to get NSTask output as soon as it appears not waiting until process finishes. I've found this answer but how it should be modified to get data ASAP? I think i should run background thread and wait for output all the time somehow.

Community
  • 1
  • 1
4ntoine
  • 19,816
  • 21
  • 96
  • 220

1 Answers1

4

You can register for the NSFileHandleDataAvailableNotification notification to read asynchronously from the task output. Example:

NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/bin/ls"];
[task setCurrentDirectoryPath:@"/"];

NSPipe *stdoutPipe = [NSPipe pipe];
[task setStandardOutput:stdoutPipe];

NSFileHandle *stdoutHandle = [stdoutPipe fileHandleForReading];
[stdoutHandle waitForDataInBackgroundAndNotify];
id observer = [[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleDataAvailableNotification
                                                                object:stdoutHandle queue:nil
                                                            usingBlock:^(NSNotification *note) 
{
    // This block is called when output from the task is available.

    NSData *dataRead = [stdoutHandle availableData];
    NSString *stringRead = [[NSString alloc] initWithData:dataRead encoding:NSUTF8StringEncoding];
    NSLog(@"output: %@", stringRead);

    [stdoutHandle waitForDataInBackgroundAndNotify];
}];

[task launch];
[task waitUntilExit];
[[NSNotificationCenter defaultCenter] removeObserver:observer];

Alternatively, you can read on a background thread, for example with GCD:

NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/bin/ls"];
[task setCurrentDirectoryPath:@"/"];

NSPipe *stdoutPipe = [NSPipe pipe];
[task setStandardOutput:stdoutPipe];

NSFileHandle *stdoutHandle = [stdoutPipe fileHandleForReading];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
    NSData *dataRead = [stdoutHandle availableData];
    while ([dataRead length] > 0) {
        NSString *stringRead = [[NSString alloc] initWithData:dataRead encoding:NSUTF8StringEncoding];
        NSLog(@"output: %@", stringRead);
        dataRead = [stdoutHandle availableData];
    }
});

[task launch];
[task waitUntilExit];
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • thanks for great answer! BTW. What is preferred approach and why? – 4ntoine May 29 '14 at 16:40
  • 1
    With the first (notification based) method, the block is called on the main thread, which may be relevant if you do any UI updates or other things which are only allowed on the main thread. Otherwise I don't know an advantage of one method over the other. – Martin R May 29 '14 at 16:52
  • I occasionally get an exception with the second version: "NSConcreteFileHandle availableData]: Resource temporarily unavailable" – Thomas Tempelmann Oct 04 '19 at 20:20