Question

task = [NSTask new];
[task setLaunchPath:@"/bin/sh"];
[task setArguments:[NSArray arrayWithObject:@"/applications/jarvis/brain/server.sh"]];
[task setCurrentDirectoryPath:@"/"];

NSPipe *outputPipe = [NSPipe pipe];
[task setStandardInput:[NSPipe pipe]];
[task setStandardOutput:outputPipe];

[task launch];

NSMutableString *outputString = [NSMutableString string];
while ([outputString rangeOfString:@"Jarvis>"].location == NSNotFound) {
    [outputString appendString:[[[NSString alloc] initWithData:[[outputPipe fileHandleForReading] readDataToEndOfFile] encoding:NSUTF8StringEncoding] autorelease]];
     }

NSArray* substrings = [outputString componentsSeparatedByString:@"Jarvis>"];
NSString* finalCharlieOutputNSTask = [substrings lastObject];
NSSpeechSynthesizer * syn = [[NSSpeechSynthesizer alloc] init];
[syn startSpeakingString:finalCharlieOutputNSTask]; 
self.charlieOutput.stringValue = finalCharlieOutputNSTask;

Ok, so that's my code. It launches an SH file and reads the output. BUT, I want it to wait until "Jarvis>" appears in the string before saying and printing the result. But, it seems like with the while loop, my code freezes there. Without it it reads the normal output of launching the server.sh file, but the whole thing. Any ideas why this doesn't work?

Here is the Server.sh file:

echo Starting Jarvis Program D.
ALICE_HOME=.
SERVLET_LIB=lib/servlet.jar
ALICE_LIB=lib/aliceserver.jar
JS_LIB=lib/js.jar

# Set SQL_LIB to the location of your database driver.
SQL_LIB=lib/mysql_comp.jar

# These are for Jetty; you will want to change these if you are using a different http server.
 HTTP_SERVER_LIBS=lib/org.mortbay.jetty.jar

 PROGRAMD_CLASSPATH=$SERVLET_LIB:$ALICE_LIB:$JS_LIB:$SQL_LIB:$HTTP_SERVER_LIBS
 java -classpath $PROGRAMD_CLASSPATH -Xms64m -Xmx128m org.alicebot.server.net.AliceServer $1
Was it helpful?

Solution

Elijah, edit your code to look like this:

...
[task launch];

NSMutableString *outputString = [NSMutableString string];
while ([outputString rangeOfString:@"Jarvis>"].location == NSNotFound) {
    [outputString appendString:[[[NSString alloc] initWithData:[[outputPipe fileHandleForReading] readDataToEndOfFile] encoding:NSUTF8StringEncoding] autorelease];
}

if ([task isRunning]) {
    [task terminate];
}

NSArray *subStrings = [outputString componentsSeparatedByString:@"Jarvis>"];
...

This code within the loop will successively read output data from your file and append it to the current output, and stop reading when @"Jarvis>" is found (it will also kill the task after a @"Jarvis>" is found, so it doesn't keep running forever).

OTHER TIPS

You want to put the readDataToEndOfFile inside the loop. Otherwise, it reads the data once, checks for existence of your string, then loops forever if it doesn't find it in that first reading.

Maybe try flushing NSPipe along the lines of code published in the article:

"NSTasks, NSPipes, and deadlocks when reading...",

http://dev.notoptimal.net/2007/04/nstasks-nspipes-and-deadlocks-when.html

Yet another approach could test the use of NSTask & NSUnbufferedIO.

You will find some example code in the book "Cocoa Programming" by Don Yacktman.

# http://www.cocoaprogramming.net
# http://www.cocoaprogramming.net/CocoaProgramming-20021010.tgz

open -e CocoaProgramming-20021010/Chapter\ 24/Animal/AnimalController.h \
        CocoaProgramming-20021010/Chapter\ 24/Animal/AnimalController.m \
        CocoaProgramming-20021010/Chapter\ 24/Calendar/CalendarController.m

# for yet another try with sample code using waitForDataInBackgroundAndNotify see:
# http://cocoawithlove.com/2009/05/invoking-other-processes-in-cocoa.html

open -e OpenFileKiller/NSTask+OneLineTasksWithOutput.m
# --> [standardOutputFile waitForDataInBackgroundAndNotify];

For non-blocking IO code for NSTask & NSFileManager also see the source code of DOCtor:

# http://www.stone.com/DOCtor/
# http://www.stone.com/DOCtor/DOCtor.tar.gz

open -e DOCTor/NSFileHandle_CFRNonBlockingIO.h \
        DOCTor/NSFileHandle_CFRNonBlockingIO.m \
        DOCTor/NSFileManager-Extensions.h \
        DOCTor/NSFileManager-Extensions.m

Also note that many command line programs (internally) block buffer their output to stdout if stdout is a pipe and not an interactive terminal (tty). For this reason some command line programs have special options to line-buffer their output regardless of sending output to a pipe or tty.

tcpdump -l
grep --line-buffered
...
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top