2014-12-02 63 views
0

我寫了一個NSTask異步執行方法爲簡單的python腳本。無法從NSTask的stdout獲得中間輸出?

然後python腳本只是打印到標準輸出,一切都很好。 當那裏有一個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 @""; 
} 

附:我到處尋找解決方案,但似乎沒有發現任何東西。它看起來像有很多NSTask演練和教程,但 - 有趣的巧合 - 他們通常避免處理任何stdin影響

回答

2

這與父進程沒有任何關係(與NSTask對象)。這都是關於子進程的行爲。子進程實際上不是將字節寫入管道(還)。

stdio man page

最初,標準錯誤流是無緩衝;標準輸入和輸出 數據流是完全緩衝的,當且僅當數據流不涉及由isatty(3)函數確定的交互式或「終端」設備。實際上,全部指代終端設備 的新近開放的流默認爲行緩衝,並且每當讀取這樣的輸入流時自動將等待輸出到這樣的流寫入 。請注意,此 僅適用於「真讀」;如果讀取請求可以通過 現有的緩衝數據來滿足,則不會發生自動刷新。在這些情況下,或者在輸出終端上打印 行的一部分之後完成大量計算時,在跳出和計算之前需要清除(3)標準 輸出,以便輸出顯示。 另外,這些默認值可以通過setvbuf(3)函數進行修改。

就你而言,標準輸入和輸出實際上不是指交互式/終端設備。因此,標準輸出是完全緩衝的(又稱爲塊緩衝,與線路緩衝或非緩衝相反)。只有在stdio庫內部的緩衝區已滿時,流關閉時或在流上調用fflush()時纔會刷新它。

您所期待的行爲,即讀取輸入時自動刷新標準輸出的行爲僅在流連接到交互式/終端設備的情況下發生。

父進程沒有辦法影響子進程的緩衝行爲,除了使用僞終端設備而不是輸入和輸出的管道。但是,這是一個複雜而容易出錯的任務。除此之外,子進程必須進行編碼以設置標準輸出的緩衝模式或定期刷新它。