2012-11-15 56 views
6

我正在编写一个Cocoa应用程序,它需要执行一个UNIX程序并逐行读取它的输出,因为它们是在生成的。我成立了一个NSTask和NSPipe这样:NSTask/NSPipe从Unix命令中读取

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

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

EDIT:与-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 ...*/ 
    } 
} 

编辑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]; 
+0

发布您的所有代码(例如您如何定义NSFileHandleDataAvailableNotification)。 –

+1

'typedef(^ notification_block_t)(NSNotification *);',当然。 – Actorclavilis

回答

8

下面是如何我通常做工作的例子:

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]){ 
     ... 
    } 
} 
+0

另外,你如何设置参数(请发布代码)? –

+1

这很好,但是它在4096字节块中读取我命令的输出,这不是最优的,因为我希望只要'tshark'产生它就可以得到数据。 – Actorclavilis

+0

此外,我添加了'-map:'作为'NSArray'作为一个类别。 – Actorclavilis

1

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

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]; 
}]; 

希望可以帮助别人。