Here is an example of an async NSTask running a unix script. Within the Unix script there are echo
commands that send back the current status to standard error like this:
echo "working" >&2
This is processed by notification center and sent to the display.
To update a determinate progress bar just send status updates like "25.0" "26.0" and convert to float and send to the progress bar.
note: I got this working after alot of experimenting and by using many tips from this site and other references. so I hope it is helpful to you.
Here are the declarations:
NSTask *unixTask;
NSPipe *unixStandardOutputPipe;
NSPipe *unixStandardErrorPipe;
NSPipe *unixStandardInputPipe;
NSFileHandle *fhOutput;
NSFileHandle *fhError;
NSData *standardOutputData;
NSData *standardErrorData;
Here are the main program modules:
- (IBAction)buttonLaunchProgram:(id)sender {
[_unixTaskStdOutput setString:@"" ];
[_unixProgressUpdate setStringValue:@""];
[_unixProgressBar startAnimation:sender];
[self runCommand];
}
- (void)runCommand {
//setup system pipes and filehandles to process output data
unixStandardOutputPipe = [[NSPipe alloc] init];
unixStandardErrorPipe = [[NSPipe alloc] init];
fhOutput = [unixStandardOutputPipe fileHandleForReading];
fhError = [unixStandardErrorPipe fileHandleForReading];
//setup notification alerts
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:@selector(notifiedForStdOutput:) name:NSFileHandleReadCompletionNotification object:fhOutput];
[nc addObserver:self selector:@selector(notifiedForStdError:) name:NSFileHandleReadCompletionNotification object:fhError];
[nc addObserver:self selector:@selector(notifiedForComplete:) name:NSTaskDidTerminateNotification object:unixTask];
NSMutableArray *commandLine = [NSMutableArray new];
[commandLine addObject:@"-c"];
[commandLine addObject:@"/usr/bin/kpu -ca"]; //put your script here
unixTask = [[NSTask alloc] init];
[unixTask setLaunchPath:@"/bin/bash"];
[unixTask setArguments:commandLine];
[unixTask setStandardOutput:unixStandardOutputPipe];
[unixTask setStandardError:unixStandardErrorPipe];
[unixTask setStandardInput:[NSPipe pipe]];
[unixTask launch];
//note we are calling the file handle not the pipe
[fhOutput readInBackgroundAndNotify];
[fhError readInBackgroundAndNotify];
}
-(void) notifiedForStdOutput: (NSNotification *)notified
{
NSData * data = [[notified userInfo] valueForKey:NSFileHandleNotificationDataItem];
NSLog(@"standard data ready %ld bytes",data.length);
if ([data length]){
NSString * outputString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSTextStorage *ts = [_unixTaskStdOutput textStorage];
[ts replaceCharactersInRange:NSMakeRange([ts length], 0)
withString:outputString];
}
if (unixTask != nil) {
[fhOutput readInBackgroundAndNotify];
}
}
-(void) notifiedForStdError: (NSNotification *)notified
{
NSData * data = [[notified userInfo] valueForKey:NSFileHandleNotificationDataItem];
NSLog(@"standard error ready %ld bytes",data.length);
if ([data length]) {
NSString * outputString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[_unixProgressUpdate setStringValue:outputString];
}
if (unixTask != nil) {
[fhError readInBackgroundAndNotify];
}
}
-(void) notifiedForComplete:(NSNotification *)anotification {
NSLog(@"task completed or was stopped with exit code %d",[unixTask terminationStatus]);
unixTask = nil;
[_unixProgressBar stopAnimation:self];
[_unixProgressBar viewDidHide];
if ([unixTask terminationStatus] == 0) {
[_unixProgressUpdate setStringValue:@"Success"];
}
else {
[_unixProgressUpdate setStringValue:@"Terminated with non-zero exit code"];
}
}
@end