2012-11-15 53 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]; 
}]; 

希望可以幫助別人。