可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Here is my code:
task = [[NSTask alloc] init]; [task setCurrentDirectoryPath:@"/applications/jarvis/brain/"]; [task setLaunchPath:@"/applications/jarvis/brain/server.sh"]; NSPipe * out = [NSPipe pipe]; [task setStandardOutput:out]; [task launch]; [task waitUntilExit]; [task release]; NSFileHandle * read = [out fileHandleForReading]; NSData * dataRead = [read readDataToEndOfFile]; NSString * stringRead = [[[NSString alloc] initWithData:dataRead encoding:NSUTF8StringEncoding] autorelease];
So I'm trying to replicate this:
cd /applications/jarvis/brain/ ./server.sh
but using NSTask in objective-c.
For some reason though, when I run this code, stringRead, returns nothing. It should return what terminal is returning when I launch the .sh file. Correct?
Any ideas?
Elijah
回答1:
Xcode Bug
There's a bug in Xcode that stops it from printing any output after a a new task using standard output is launched (it collects all output, but no longer prints anything). You're going to have to call [task setStandardInput:[NSPipe pipe]] to get it to show output again (or, alternatively, have the task print to stderr instead of stdout).
Suggestion for final code:
NSTask *server = [NSTask new]; [server setLaunchPath:@"/bin/sh"]; [server setArguments:[NSArray arrayWithObject:@"/path/to/server.sh"]]; [server setCurrentDirectoryPath:@"/path/to/current/directory/"]; NSPipe *outputPipe = [NSPipe pipe]; [server setStandardInput:[NSPipe pipe]]; [server setStandardOutput:outputPipe]; [server launch]; [server waitUntilExit]; // Alternatively, make it asynchronous. [server release]; NSData *outputData = [[outputPipe fileHandleForReading] readDataToEndOfFile]; NSString *outputString = [[[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding] autorelease]; // Autorelease optional, depending on usage.
回答2:
The solution above is freezing because it's synchronous. Call to [server waitUntilExit] blocks the run loop until the tasks is done.
Here's the async solution for getting the task output.
task.standardOutput = [NSPipe pipe]; [[task.standardOutput fileHandleForReading] setReadabilityHandler:^(NSFileHandle *file) { NSData *data = [file availableData]; // this will read to EOF, so call only once NSLog(@"Task output! %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); // if you're collecting the whole output of a task, you may store it on a property [self.taskOutput appendData:data]; }];
Probably you want to repeat the same above for task.standardError.
IMPORTANT:
When your task terminates, you have to set readabilityHandler block to nil; otherwise, you'll encounter high CPU usage, as the reading will never stop.
[task setTerminationHandler:^(NSTask *task) { // do your stuff on completion [task.standardOutput fileHandleForReading].readabilityHandler = nil; [task.standardError fileHandleForReading].readabilityHandler = nil; }];
This is all asynchronous (and you should do it async), so your method should have a ^completion block.