【问题标题】:NSTask/NSPipe read from Unix commandNSTask/NSPipe 从 Unix 命令读取
【发布时间】:2012-11-03 17:49:50
【问题描述】:

我正在编写一个 Cocoa 应用程序,它需要执行一个 UNIX 程序并在生成时逐行读取其输出。我这样设置了一个 NSTask 和 NSPipe:

task = [[NSTask alloc] init];
pipe = [NSPipe pipe];
[task setStandardOutput:pipe];
//... later ...
[task setArguments:...];
[task setLaunchPath:@"..."];
[task launch];
handle = [[task fileHandleForReading] retain];

在程序用[task terminate] 告诉它这样做之前,该命令不会终止。我尝试了几种从句柄读取的方法,例如-readInBackgroundAndNotifywhile([(data = [handle availableData]) length] > 0)-waitForDataInBackgroundAndNotify,但管道似乎从未产生任何数据。有什么方法可以“戳”NSTaskNSPipe 以刷新数据?

编辑-readInBackgroundAndNotify

[handle readInBackgroundAndNotify];
notification_block_t handlerBlock =
    ^(NSNotification *notification) {
         NSData *data = [[notification userInfo]
                             objectForKey: NSFileHandleNotificationDataItem];
         /*... do stuff ...*/
         [self addNotification: handle block: handlerBlock];
     };
[self addNotification: handler block: handlerBlock];
//...
- (void)addNotification:(id)handle block:(notification_block_t)block {
    [[NSNotificationCenter defaultCenter]
         addObserverForName: NSFileHandleReadCompletionNotification
         object: handle
         queue: [NSOperationQueue mainQueue]
         usingBlock: block];
}

-waitForDataInBackgroundAndNotify:

[handle waitForDataInBackgroundAndNotify];
notification_block_t handlerBlock =
    ^(NSNotification *notification) {
        NSData *data = [handle availableData];
        /*... do stuff ...*/
    };
[self addNotification: handler block: handlerBlock];

使用while 循环:

[self startProcessingThread: handle];
//...
- (void)startProcessingThread:(NSFileHandle *)handle {
    [[NSOperationQueue mainQueue]
         addOperation: [[[NSInvocationOperation alloc]
                             initWithTarget: self
                             selector: @selector(dataLoop:)
                             object: handle] autorelease]];
}
- (void)dataLoop:(NSFileHandle *)handle {
    NSData *data;
    while([(data = [handle availableData]) length] > 0) {
        /*... do stuff ...*/
    }
}

EDIT 2:参数设置如下(命令为tshark):

NSArray *cmd = [NSArray arrayWithObjects:@"-R", @"http.request", 
                                         @"-Tfields", @"-Eseparator='|'", 
                                         @"-ehttp.host", @"-ehttp.request.method", 
                                         @"-ehttp.request.uri", nil];
cmd = [[cmd arrayByAddingObjectsFromArray:[self.ports map:^(id arg1, NSUInteger idx) {
           return [NSString stringWithFormat:@"-d tcp.port==%d,http", [arg1 intValue]];
       }]] 
        arrayByAddingObject:[@"dst " stringByAppendingString:
            [self.hosts componentsJoinedByString:@" or dst "]]];
[self.tsharktask setArguments:cmd];

【问题讨论】:

  • 发布所有代码(例如,您如何定义 NSFileHandleDataAvailableNotification)。
  • typedef (^notification_block_t)(NSNotification *);,当然。

标签: objective-c cocoa nstask nspipe


【解决方案1】:

这是我通常如何做的一个工作示例:

    task = [[NSTask alloc] init];
    [task setLaunchPath:...];
    NSArray *arguments;
    arguments = ...;
    [task setArguments:arguments];

    NSPipe *outPipe;
    outPipe = [NSPipe pipe];
    [task setStandardOutput:outPipe];

    outFile = [outPipe fileHandleForReading];
    [outFile waitForDataInBackgroundAndNotify];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(commandNotification:)
                                                 name:NSFileHandleDataAvailableNotification 
                                               object:nil];    

    [task launch];


- (void)commandNotification:(NSNotification *)notification
{
    NSData *data = nil;
    while ((data = [self.outFile availableData]) && [data length]){
        ...
    }   
}

【讨论】:

  • 这很好用,但它以 4096 字节块读取我的命令的输出,这不是最佳的,因为我希望在tshark 生成数据后立即获得数据。跨度>
  • 我还将-map: 添加到NSArray 作为一个类别。
  • 参数没问题。尝试使用您在 NSFileHandle 文档的“异步通信”段落中看到的方法:developer.apple.com/library/ios/#documentation/Cocoa/Reference/…,替换 waitForDataInBackgroundAndNotify。
【解决方案2】:

这是获取任务输出的异步解决方案。

task.standardOutput = [NSPipe pipe];
[[task.standardOutput fileHandleForReading] setReadabilityHandler:^(NSFileHandle *file) {
NSData *data = [file availableData]; // this will read to EOF, so call only once
NSLog(@"Task output! %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);

// if you're collecting the whole output of a task, you may store it on a property
//maybe you want to appenddata
//[weakself.taskOutput appendData:data];
}];

希望可以帮助某人。

【讨论】:

    猜你喜欢
    • 2011-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多