我为一个简单的python脚本编写了一个NSTask
async exec方法。
当python脚本只打印到stdout时,一切都很好。
如果有raw_input
(期望来自用户的输入),它肯定会使输入正常,但它不会在raw_input
之前打印数据。
发生了什么事?
- (NSString*)exec:(NSArray *)args environment:(NSDictionary*)env action:(void (^)(NSString*))action completed:(void (^)(NSString*))completed
{
_task = [NSTask new];
_output = [NSPipe new];
_error = [NSPipe new];
_input = [NSPipe new];
NSFileHandle* outputF = [_output fileHandleForReading];
NSFileHandle* errorF = [_error fileHandleForReading];
NSFileHandle* inputF = [_input fileHandleForWriting];
__block NSString* fullOutput = @"";
NSMutableDictionary* envs = [NSMutableDictionary dictionary];
NSArray* newArgs = @[@"bash",@"-c"];
[_task setLaunchPath:@"/usr/bin/env"];
if (env)
for (NSString* key in env)
envs[key] = env[key];
if ([envs count]) [_task setEnvironment:envs];
NSString* cmd = @"";
cmd = [cmd stringByAppendingString:[[[self sanitizedArgs:args] componentsJoinedByString:@" "] stringByAppendingString:@" && echo \":::::$PWD:::::\""]];
[_task setArguments:[newArgs arrayByAddingObject:cmd]];
[_task setStandardOutput:_output];
[_task setStandardError:_error];
[_task setStandardInput:_input];
void (^outputter)(NSFileHandle*) = ^(NSFileHandle *file){
NSData *data = [file availableData];
NSString* str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"Output: %@",str);
action(str);
fullOutput = [fullOutput stringByAppendingString:str];
};
void (^inputter)(NSFileHandle*) = ^(NSFileHandle *file) {
NSLog(@"In inputter");
NSData *data = [[_task.standardOutput fileHandleForReading] availableData];
NSString* str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"Output: %@",str);
};
[outputF setReadabilityHandler:outputter];
[errorF setReadabilityHandler:outputter];
//[inputF setWriteabilityHandler:inputter];
[_task setTerminationHandler:^(NSTask* task){
NSLog(@"Terminated: %@",fullOutput);
completed(fullOutput);
//[task.standardOutput fileHandleForReading].readabilityHandler = nil;
//[task.standardError fileHandleForReading].readabilityHandler = nil;
//[task.standardInput fileHandleForWriting].writeabilityHandler = nil;
//[task terminate];
//task = nil;
}];
[_task launch];
//[[_input fileHandleForWriting] waitForDataInBackgroundAndNotify];
return @"";
}
P.S。我到处寻找解决方案,但似乎没有发现任何问题。看起来有大量的NSTask
演练和教程,但是 - 有趣的巧合 - 它们通常避免处理任何stdin
含义
答案 0 :(得分:2)
这与父进程(具有NSTask
对象的进程)无关。这完全是关于子进程的行为。子进程实际上并没有将字节写入管道(还)。
最初,标准错误流是无缓冲的;标准输入和输出 当且仅当流不引用时,才完全缓冲流 交互式或“终端”设备,由isatty(3)函数确定。 事实上,所有新开的流都引用了终端设备 默认为行缓冲,并写入对这些流的挂起输出 每当读取这样的输入流时自动进行。请注意这一点 仅适用于``true reads'&#39 ;;如果读取请求可以满足 现有的缓冲数据,不会发生自动刷新。在这些情况下, 或者在打印部分文件后进行大量计算 在输出端子上的线路,必须fflush(3)标准 在关闭和计算之前输出,以便输出出现。 或者,可以通过setvbuf(3)函数修改这些默认值。
在您的情况下,标准输入和输出实际上并不是指交互式/终端设备。因此,标准输出是完全缓冲的(即缓冲块,而不是行缓冲或无缓冲)。只有当stdio库内部的缓冲区已满,当流关闭或者在流上调用fflush()
时才会刷新它。
您在阅读输入时自动刷新标准输出的行为只会在流连接到交互式/终端设备的情况下发生。
除了使用伪终端设备而不是管道进行输入和输出之外,父进程无法影响子进程的缓冲行为。然而,这是一项复杂且容易出错的事业。除此之外,必须对子进程进行编码以设置标准输出的缓冲模式或定期刷新它。