无法从NSTask的stdout获得中间输出? [英] Unable to get intermediate output from NSTask's stdout?

查看:103
本文介绍了无法从NSTask的stdout获得中间输出?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经为简单的python脚本编写了NSTask异步exec方法.

I have written an NSTask async exec method for a simple python script.

然后将python脚本仅打印到stdout时,一切都很好. 当其中有raw_input时(期望来自用户的输入),它肯定可以很好地进行输入,但不会在raw_input之前打印数据.

When then python script just prints to stdout, all is fine. When there is a raw_input in there (expecting input from the user), it sure gets the input fine, but it does NOT print the data BEFORE 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 @"";
}


PS .我到处都在寻找解决方案,但似乎什么也没发现.看起来有很多NSTask演练和教程,但是-有趣的巧合-他们通常避免处理任何stdin含义


P.S. I've searched everywhere for a solution, but didn't seem to spot anything. It looks like there are tons of NSTask walkthroughs and tutorials, but - funny coincidence - they usually avoid dealing with any of the stdin implications

推荐答案

这与父进程(与NSTask对象有关的父进程)无关.这全都与子进程的行为有关.子进程实际上并没有将字节写入管道.

This doesn't have anything to do with the parent process (the one with the NSTask object). It's all about the behavior of the child process. The child process is literally not writing the bytes to the pipe (yet).

stdio手册页:

最初,标准错误流是无缓冲的;标准输入和输出 当且仅当流不引用流时,流才被完全缓冲 由isatty(3)函数确定的交互式或终端"设备. 实际上, all 所有新打开的流都指向终端设备 默认为行缓冲,并且将待处理的输出写入此类流 每当读取这样的输入流时,它就会自动生成.请注意, 仅适用于``真读'';如果读取请求可以满足 现有的缓冲数据,将不会发生自动刷新.在这些情况下, 或在打印部分 如果在输出端子上连接线,则必须对标准进行fflush(3) 输出,然后再进行计算,以便显示输出. 另外,可以通过setvbuf(3)函数修改这些默认值.

Initially, the standard error stream is unbuffered; the standard input and output streams are fully buffered if and only if the streams do not refer to an interactive or "terminal" device, as determined by the isatty(3) function. In fact, all freshly-opened streams that refer to terminal devices default to line buffering, and pending output to such streams is written automatically whenever such an input stream is read. Note that this applies only to ``true reads''; if the read request can be satisfied by existing buffered data, no automatic flush will occur. In these cases, or when a large amount of computation is done after printing part of a line on an output terminal, it is necessary to fflush(3) the standard output before going off and computing so that the output will appear. Alternatively, these defaults may be modified via the setvbuf(3) function.

在您的情况下,标准输入和输出实际上并不涉及交互式/终端设备.因此,标准输出是完全缓冲的(又称块缓冲,而不是行缓冲或非缓冲).仅在stdio库内部的缓冲区已满,流关闭或在流上调用fflush()时才刷新.

In your case, the standard input and output do not, in fact, refer to an interactive/terminal device. So, standard output is fully buffered (a.k.a. block buffered, as opposed to line buffered or unbuffered). It is only flushed when the buffer internal to the stdio library is full, when the stream is closed, or when fflush() is called on the stream.

您期望的行为,即在读取输入时会自动刷新标准输出,仅在流连接到交互式/终端设备的情况下才会发生.

The behavior you're expecting, where standard output is flushed automatically when input is read, only happens in the case where the streams are connected to an interactive/terminal device.

父进程没有办法影响子进程的这种缓冲行为,除非使用伪终端设备,而不是使用管道作为输入和输出.但是,这是一个复杂且容易出错的工作.除此之外,还必须对子进程进行编码,以设置标准输出的缓冲模式或定期刷新它.

There is no way for the parent process to influence this buffering behavior of the child process except by using a pseudo-terminal device rather than a pipe for the input and output. However, that's a complex and error-prone undertaking. Other than that, the child process has to be coded to set the buffering mode of standard output or flush it regularly.

这篇关于无法从NSTask的stdout获得中间输出?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆