- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我通过 NSTask
调用各种命令行工具。这些工具可能会运行几秒钟,并不断地将文本输出到stdout
。最终,该工具将自行终止。我的应用使用 readInBackgroundAndNotify
异步读取其输出。
如果我在工具退出后立即停止处理异步输出,我通常会丢失一些尚未交付的输出。
这意味着我必须等待更长的时间,让 RunLoop 处理挂起的读取通知。 如何判断我何时已读取该工具写入管道的所有内容?
可以在下面的代码中通过删除带有 runMode:
调用的行来验证此问题 - 然后程序将打印已处理的零行。因此,看起来在进程退出时,队列中已经有一个通知正在等待传递,并且该传递是通过 runMode:
调用进行的。
现在,看起来在工具退出后简单地调用 runMode:
once 可能就足够了,但我的测试表明它不是 - 有时(对于更大的输出数据量),这仍然只会处理剩余数据的一部分。
注意:诸如使调用的工具输出某些文本结束标记之类的解决方法不是我寻求的解决方案。我相信必须有某种正确的方法来做到这一点,从而以某种方式发出管道流结束的信号,这就是我正在寻找的答案。
下面的代码可以粘贴到新 Xcode 项目的 AppDelegate.m
文件中。
运行时,它会调用一个生成较长输出的工具,然后使用 waitUntilExit
等待该工具终止。如果随后立即删除 outputFileHandleReadCompletionObserver
,则该工具的大部分输出都会丢失。通过添加持续一秒的 runMode:
调用,可以接收该工具的所有输出 - 当然,这个定时循环并不是最佳的。
我希望保持 runModal 函数同步,即在收到工具的所有输出之前它不会返回。如果这很重要的话,它确实在我的实际程序中以自己的方式运行(我看到 Peter Hosey 的评论警告说 waitUntilExit
会阻塞 UI,但这对我来说不是问题)。
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self runTool];
}
- (void)runTool
{
// Retrieve 200 lines of text by invoking `head -n 200 /usr/share/dict/words`
NSTask *theTask = [[NSTask alloc] init];
theTask.qualityOfService = NSQualityOfServiceUserInitiated;
theTask.launchPath = @"/usr/bin/head";
theTask.arguments = @[@"-n", @"200", @"/usr/share/dict/words"];
__block int lineCount = 0;
NSPipe *outputPipe = [NSPipe pipe];
theTask.standardOutput = outputPipe;
NSFileHandle *outputFileHandle = outputPipe.fileHandleForReading;
NSString __block *prevPartialLine = @"";
id <NSObject> outputFileHandleReadCompletionObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleReadCompletionNotification object:outputFileHandle queue:nil usingBlock:^(NSNotification * _Nonnull note)
{
// Read the output from the cmdline tool
NSData *data = [note.userInfo objectForKey:NSFileHandleNotificationDataItem];
if (data.length > 0) {
// go over each line
NSString *output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSArray *lines = [[prevPartialLine stringByAppendingString:output] componentsSeparatedByString:@"\n"];
prevPartialLine = [lines lastObject];
NSInteger lastIdx = lines.count - 1;
[lines enumerateObjectsUsingBlock:^(NSString *line, NSUInteger idx, BOOL * _Nonnull stop) {
if (idx == lastIdx) return; // skip the last (= incomplete) line as it's not terminated by a LF
// now we can process `line`
lineCount += 1;
}];
}
[note.object readInBackgroundAndNotify];
}];
NSParameterAssert(outputFileHandle);
[outputFileHandle readInBackgroundAndNotify];
// Start the task
[theTask launch];
// Wait until it is finished
[theTask waitUntilExit];
// Wait one more second so that we can process any remaining output from the tool
NSDate *endDate = [NSDate dateWithTimeIntervalSinceNow:1];
while ([NSDate.date compare:endDate] == NSOrderedAscending) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}
[[NSNotificationCenter defaultCenter] removeObserver:outputFileHandleReadCompletionObserver];
NSLog(@"Lines processed: %d", lineCount);
}
最佳答案
这很简单。在观察者 block 中,当data.length
为0时,删除观察者并调用terminate
。
代码将在 waitUntilExit
行之后继续。
- (void)runTool
{
// Retrieve 20000 lines of text by invoking `head -n 20000 /usr/share/dict/words`
const int expected = 20000;
NSTask *theTask = [[NSTask alloc] init];
theTask.qualityOfService = NSQualityOfServiceUserInitiated;
theTask.launchPath = @"/usr/bin/head";
theTask.arguments = @[@"-n", [@(expected) stringValue], @"/usr/share/dict/words"];
__block int lineCount = 0;
__block bool finished = false;
NSPipe *outputPipe = [NSPipe pipe];
theTask.standardOutput = outputPipe;
NSFileHandle *outputFileHandle = outputPipe.fileHandleForReading;
NSString __block *prevPartialLine = @"";
[[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleReadCompletionNotification object:outputFileHandle queue:nil usingBlock:^(NSNotification * _Nonnull note)
{
// Read the output from the cmdline tool
NSData *data = [note.userInfo objectForKey:NSFileHandleNotificationDataItem];
if (data.length > 0) {
// go over each line
NSString *output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSArray *lines = [[prevPartialLine stringByAppendingString:output] componentsSeparatedByString:@"\n"];
prevPartialLine = [lines lastObject];
NSInteger lastIdx = lines.count - 1;
[lines enumerateObjectsUsingBlock:^(NSString *line, NSUInteger idx, BOOL * _Nonnull stop) {
if (idx == lastIdx) return; // skip the last (= incomplete) line as it's not terminated by a LF
// now we can process `line`
lineCount += 1;
}];
} else {
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSFileHandleReadCompletionNotification object:nil];
[theTask terminate];
finished = true;
}
[note.object readInBackgroundAndNotify];
}];
NSParameterAssert(outputFileHandle);
[outputFileHandle readInBackgroundAndNotify];
// Start the task
[theTask launch];
// Wait until it is finished
[theTask waitUntilExit];
// Wait until all data from the pipe has been received
while (!finished) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.0001]];
}
NSLog(@"Lines processed: %d (should be: %d)", lineCount, expected);
}
关于macos - NSTask 结束后如何读取 readInBackgroundAndNotify 的所有剩余输出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58459095/
我正在运行一个带有 while 约束的 SQL 查询,其中包含一些“id”。例如: SELECT table.id FROM TableOne table WHERE table.id IN (1,
我正在寻找在替换其中一个元素后打印元素列表的最正确方法。我可以按如下方式做,但显然很困惑。 #!/usr/bin/python import sys file = open(sys.argv[1])
这个问题在这里已经有了答案: How wide is the default `` margin? (4 个答案) How do I remove the top margin in a web
当我尝试使用命令安装 rvm 时::(I am Using UBUNTU 12.04 LTS) curl -L https://get.rvm.io | bash -s 当我尝试与简单用户相同的命令时
我使用 GOPro 工作人员 6 个月前发送给我的命令,通过终端(在 Gopro 网络上)使用 Gopro Hero3 拍摄照片/视频。有效。但是,在过去的一个月里,我一直在尝试再次执行此操作,并且不
尽管知道我不应该关闭应用程序按钮,但我仍然这样做。完成所有 Activity 后,我调用 finish() 方法,它们调用析构函数和所有内容。用户的行为也是正确的。但我想知道为什么还有 5 个打开的线
当我在 Rest Controller 中的类级别启用 @Validated spring 注释时,会生成 2 个验证上下文(每个验证上下文都有不同的前缀)。 @Validated 注释是必需的,因为
在旧的 API 中,剩余的允许容量显然作为 X-Ratelimit-Remaining 返回HTTP header 。 然而,current version's documentation对此一无所获
我一直在使用 Service Fabric 一段时间,成功构建、部署和测试了多个服务,但我刚刚完成构建的服务在部署时失败(请参阅下面的错误)。在诊断中,我尝试使用 VS 模板(没有代码更改)创建和部署
这个问题在这里已经有了答案: 关闭 12 年前。 Possible Duplicate: Progress unit in ProgressDialog 如何覆盖进度条进度消息,即 61/100 到
我正在用 Objective-C (Cocoa) 编写命令行实用程序的前端。我需要解析输出以检查不同类型的消息。有两种基本类型;信息消息和下载状态消息。信息消息始终以以下内容之一开头:INFO:、WA
我是一名优秀的程序员,十分优秀!