【问题标题】:NSTask Does Not TerminateNSTask 不终止
【发布时间】:2023-03-03 19:17:01
【问题描述】:

我正在尝试使用 NSTask 运行 UNIX 'apropos' 命令。这是我的代码:

NSTask *apropos = [[NSTask alloc] init];
NSPipe *pipe = [[NSPipe alloc] init];
[apropos setLaunchPath:@"/usr/bin/apropos"];
[apropos setArguments:[NSArray arrayWithObjects:@"filename", @"match", nil]];
[apropos setStandardOutput:pipe];
[apropos launch];
[apropos waitUntilExit];

问题是这永远不会返回。我还尝试使用 Apple 的示例代码 (TaskWrapper),它返回输出(分三段),但它从不调用 processFinished 处理程序。

此外,appendOutput: 处理程序接收重复​​项。因此,例如,如果 apropos 返回:

1 2 3 4 5

我可能会收到这样的东西:

1 2 3

1 2 3 4

5

(分为 3 条附加消息)。

我注意到 Apropos 以一种可以在命令行中上下滚动的格式显示输出,而不是直接将数据直接输出到标准输出;如何通过 NSTask 和 NSPipe 可靠地读取此内容?

【问题讨论】:

  • 关于滚动输出,我假设 apropos 正在通过 less 管道输出其输出;有没有办法强迫它不这样做?这可能是解决方案。
  • apropos 使用默认为 less 的 PAGER 环境变量,因此将 PAGER 设置为更贴切的东西,比如 cat 会起作用:)
  • 嗯,这个应用不能修改环境变量(它是给用户安装的应用)。

标签: cocoa unix pipe nstask


【解决方案1】:

我刚刚测试了这个程序,它运行良好:程序终止,/tmp/apropos.txt 包含apropos 的输出。

#import <Foundation/Foundation.h>

int main()
{
    NSAutoreleasePool *pool = [NSAutoreleasePool new];

    NSTask *apropos = [[[NSTask alloc] init] autorelease];
    NSPipe *pipe = [[[NSPipe alloc] init] autorelease];
    NSFileHandle *readHandle = [pipe fileHandleForReading];
    [apropos setLaunchPath:@"/usr/bin/apropos"];
    [apropos setArguments:[NSArray arrayWithObjects:@"filename", @"match", nil]];
    [apropos setStandardOutput:pipe];
    [apropos launch];
    [apropos waitUntilExit];

    NSString *output = [[[NSString alloc]
        initWithData:[readHandle readDataToEndOfFile]
            encoding:NSUTF8StringEncoding] autorelease];

    [output writeToFile:@"/tmp/apropos.txt" atomically:YES
        encoding:NSUTF8StringEncoding error:NULL];

    [pool drain];
    return 0;
}

您是否有机会使用NSLog() 来检查输出?如果是这样,您可能需要为stdin 设置管道,如in this answer of mine to an NSTask related question 所述。看来NSLog()stderr发送数据会影响NSTask

【讨论】:

  • 非常感谢。就是这样。我修改了 Apple 代码以在 NSTask 上将管道设置为标准输入,现在它在使用 NSLog 显示输出时工作正常。如果我不使用它,我会记得将来在输入上设置一个空管道。 :)
  • @bavarious:你拯救了我的一天! jfm429能否详细说明如何设置..那个管道的东西?目前我只是禁用 NSLog
  • 你为我节省了很多时间。谢谢!
【解决方案2】:

使用您的原始代码,我想这是因为您没有读取命令的输出。管道只有有限的缓冲区大小,如果您不读取任务的输出,它可能会挂起等待缓冲区清空。我对您尝试的示例代码一无所知,所以我无能为力。至于最后一个问题,apropos 仅在连接到终端时才使用寻呼机。你不是在模拟终端,所以你不必担心。您可以通过在终端中运行apropos whatever | cat 并验证没有调用寻呼机来证明这一点。

【讨论】:

  • developer.apple.com/library/mac/#samplecode/Moriarity/… 有我正在使用的示例代码。
  • 另外,我没有读错 AFAIK。如果我从终端执行apropos filename match &gt; test.txt,然后使用“/usr/cat”和“/Users/Justin/text.txt”作为 NSTask 参数(不对 NSTask 代码进行任何额外修改),那么它可以完美运行。因此,通过 NSTask 的 apropos 输出似乎以某种方式被破坏,但通过完全相同的代码从 cat 输出的处理得很好。
猜你喜欢
  • 2013-12-29
  • 1970-01-01
  • 2011-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多