简体   繁体   English

Objective-C,NSTask 缓冲区限制

[英]Objective-C, NSTask Buffer Limitation

I'm using NSTask to run an external utility which returns a long string of data.我正在使用NSTask运行一个返回一长串数据的外部实用程序。 The problem is that when the returned string exceeds a large amount of data (around 32759 chars) it becomes null or truncates the returned string.问题在于,当返回的字符串超过大量数据(大约 32759 个字符)时,它会变为null或截断返回的字符串。 How do I return the full output?如何返回完整输出?

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

To test the functionality use cat or similar on a large file for the myExternalCommand .要测试功能,请在myExternalCommand的大文件上使用cat或类似工具。 The issue seems to happen right after the character length of 32759...这个问题似乎发生在 32759 字符长度之后......

solution?解决方案? I'm not sure, but what might need to happen is to somehow read the return stdout in chunks, then append the outputString data if possible.我不确定,但可能需要发生的是以某种方式读取块中的返回stdout ,然后在可能的情况下附加outputString数据。

update: I tried moving waitUntilExit after readDataToEndOfFile per suggestion, but it did not affect the outcome.更新:我试着移动waitUntilExitreadDataToEndOfFile每建议,但它并没有影响结果。

*please note, I'm looking for an Obj-C solution, thanks. *请注意,我正在寻找Obj-C解决方案,谢谢。

Found on CocoaDev :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来自: 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 …”) NSPipe 缓冲区限制似乎是 4096 字节(参见 /usr/include/limits.h: “... #define _POSIX_ARG_MAX 4096 ...”)

You can read the output from your NSTask asynchronously, using readabilityHandler .您可以从读取输出NSTask异步,使用readabilityHandler Within the handler, use availableData to read the output piece-by-piece.在处理程序中,使用availableData逐个读取输出。

Use a terminationHandler to get notified once the task exits, and then set your readabilityHandler to nil to stop it from reading.使用终止处理程序在任务退出后获得通知,然后将您的readabilityHandler设置为 nil 以阻止它读取。

It's all async, so you'll need to block and wait until the task exits.这都是异步的,所以你需要阻塞并等待任务退出。

Here is a complete sample that works well enough for me.这是一个完整的示例,对我来说效果很好。 I used a printf instead of NSLog as it seems that NSLog is truncating the output on the console (not sure if that's a bug or a feature ).我使用printf而不是NSLog ,因为NSLog似乎正在截断控制台上的输出(不确定这是错误还是功能)。 Error checking is omitted and adds some complexity, you will probably want to read standardError as well in the same way.错误检查被省略并增加了一些复杂性,您可能也希望以相同的方式读取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);

I had a task that was throwing a large error immediately, and causing a hang, adding a reader to the stderr solved the issue我有一个任务立即抛出一个大错误并导致挂起,向 stderr 添加一个读取器解决了这个问题

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM