Objective-C,NSTask 缓冲区限制

Objective-C, NSTask Buffer Limitation

我正在使用 NSTask 到 运行 一个外部实用程序,其中 return 是一长串数据。问题是,当 returned 字符串超过大量数据(大约 32759 个字符)时,它变成 null 或 t运行cates returned 字符串。我如何 return 完整输出?

NSTask *myTask = [[NSTask alloc] init];

[myTask setLaunchPath:myExternalCommand];
[myTask setArguments:[NSArray arrayWithObjects: arg1, arg2, nil]];

NSPipe *pipe = [NSPipe pipe];
[myTask setStandardOutput:pipe];

NSFileHandle *taskHandle;
taskHandle = [pipe fileHandleForReading];

[myTask launch];
[myTask waitUntilExit];

NSData *taskData;
taskData = [taskHandle readDataToEndOfFile];

NSString *outputString = [[NSString alloc] initWithData:taskData
                         encoding:NSUTF8StringEncoding];

NSLog(@"Output: \n%@", outputString);
// (null or truncated) when stdout exceeds x amount of stdout

要测试功能,请在 myExternalCommand 的大文件上使用 cat 或类似文件。问题似乎发生在字符长度 32759 之后...

解决方案?我不确定,但可能需要以某种方式分块读取 return stdout,然后尽可能附加 outputString 数据。

更新:我尝试根据建议将 waitUntilExit 移动到 readDataToEndOfFile 之后,但这并没有影响结果。

*please note, I'm looking for an Obj-C solution, thanks.

发现于 CocoaDev

“The data that passes through the pipe is buffered; the size of the buffer is determined by the underlying operating system.”

from: http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSPipe_Class/index.html

The NSPipe buffer limit seems to be 4096 bytes (cf. /usr/include/limits.h: “… #define _POSIX_ARG_MAX 4096 …”)

您可以使用 readabilityHandler 异步读取 NSTask 的输出。在处理程序中,使用 availableData 逐个读取输出。

使用 terminationHandler 在任务退出后得到通知,然后将 readabilityHandler 设置为 nil 以停止读取。

都是异步的,所以你需要阻塞等待任务退出。

这是一个完整的示例,对我来说效果很好。我使用 printf 而不是 NSLog,因为看起来 NSLog 正在截断控制台上的输出(不确定那是否是 bug or a feature)。省略了错误检查并增加了一些复杂性,您可能也想以相同的方式阅读 standardError

dispatch_semaphore_t waitHandle;
NSTask *myTask;
NSMutableData* taskOutput;
        
waitHandle = dispatch_semaphore_create(0);
        
myTask = [[NSTask alloc] init];
[myTask setLaunchPath:@"/bin/cat"];
[myTask setArguments:[NSArray arrayWithObjects: @"/path/to/a/big/file", nil]];
[myTask setStandardOutput:[NSPipe pipe]];
        
taskOutput = [[NSMutableData alloc] init];
        
[[myTask.standardOutput fileHandleForReading] setReadabilityHandler:^(NSFileHandle *file) {
    NSData *data = [file availableData];
    [taskOutput appendData:data];
}];
        
[myTask setTerminationHandler:^(NSTask *task) {
    [task.standardOutput fileHandleForReading].readabilityHandler = nil;
            
    NSString *outputString = [[NSString alloc] initWithData:taskOutput encoding:NSUTF8StringEncoding];
    printf("Output: \n%s\n", [outputString UTF8String]);
            
    dispatch_semaphore_signal(waitHandle);
}];
        
[myTask launch];
        
dispatch_semaphore_wait(waitHandle, DISPATCH_TIME_FOREVER);

我有一个任务立即抛出一个大错误,并导致挂起,向 stderr 添加 reader 解决了这个问题

[[myTask.standardError fileHandleForReading] setReadabilityHandler:^(NSFileHandle *file) {
    NSData *data = [file availableData];
    [taskError appendData:data];
}];