2013-06-04 37 views
3

我一直在挖掘一個答案很長一段時間,但我從來沒有得到一個工作代碼。NSTask與長輸出(OSX)

我有一個代碼,使用dd來生成一個文件。有時需要幾分鐘時間,具體取決於尺寸,我認爲擁有進度條會很棒。到目前爲止,一切正常,並被捆綁起來。但是,進度條不會更新,因爲我需要不斷向其發送值。我找到了獲取當前值的方法,我設法獲取當前數據,但現在我無法在應用程序中實時獲取輸出結果,除了日誌。

到目前爲止,這是我得到的最好:

// Action: 
// dd if=/dev/zero bs=1048576 count=500 |pv -Wn -s <size>|of=/Users/me/Desktop/asd.img 
// Be careful, it generates files of 500MB!! 

NSTask * d1Task = [[NSTask alloc] init]; 
NSTask * pvTask = [[NSTask alloc] init]; 
NSTask * d2Task = [[NSTask alloc] init]; 

[d1Task setLaunchPath:@"/bin/dd"]; 
[pvTask setLaunchPath:@"/Users/me/Desktop/pv"]; 
[d2Task setLaunchPath:@"/bin/dd"]; 

// For pvTask I use something like... 
// [[NSBundle mainBundle] pathForAuxiliaryExecutable: @"pv"] 
// ... in the final version. 

[d1Task setArguments: [NSArray arrayWithObjects: 
         @"if=/dev/zero" 
         , @"bs=1048576" 
         , @"count=500" 
         , nil]]; 

[pvTask setArguments:[NSArray arrayWithObjects: 
         @"-Wn" 
         , [ NSString stringWithFormat:@"-s %d", 1048576 * 500] 
         , nil]]; 

[d2Task setArguments: [NSArray arrayWithObjects: 
         @"of=/Users/me/Desktop/file.dmg" 
         , nil]]; 

NSPipe * pipeBetween1 = [NSPipe pipe]; 
[d1Task setStandardOutput: pipeBetween1]; 
[pvTask setStandardInput: pipeBetween1]; 

NSPipe * pipeBetween2 = [NSPipe pipe]; 
[pvTask setStandardOutput: pipeBetween2]; 
[d2Task setStandardInput: pipeBetween2]; 

// Missing code here 

[d1Task launch]; 
[pvTask launch]; 
[d2Task launch]; 

在缺少規範的一部分,我試過幾件事情。首先我嘗試了一個觀察者,像這樣:

NSFileHandle * pvOutput = [pipeBetween2 fileHandleForReading]; 
[pvOutput readInBackgroundAndNotify]; 

[ [NSNotificationCenter defaultCenter] 
    addObserver:self 
     selector:@selector(outputData:) 
      name:NSFileHandleDataAvailableNotification 
      object:pvOutput 
]; 

沒有成功。我只在執行的開始或結束時收到反饋,但在執行期間仍然沒有反饋。

我也試過類似:

[pvOutput setReadabilityHandler:^(NSFileHandle *file) { 
// Stuff here 
}]; 

但是這裏沒有這樣的方法。也許我的XCode已經過時了? (我使用4.2)。

最近我一直在試用相同的通用代碼,使用/sbin/ping ping 10次服務器,但沒有成功獲取輸出。我怎樣才能做到這一點?我可以閱讀關於這個主題的任何文件嗎?

謝謝!

+0

不知道這是否是正確的解決方案。但如何在後臺線程中運行您的任務? – Mikael

+0

「pv」做什麼? –

+0

嗨馬丁,光伏是一個管道查看器。請參閱此頁:http://www.catonmat.net/blog/unix-utilities-pipe-viewer/ – Apollo

回答

2

pv工具寫入進度輸出標準錯誤,所以你應該建立 另一個管道:

NSPipe *pvStderrPipe = [NSPipe pipe]; 
[pvTask setStandardError:pvStderrPipe]; 
NSFileHandle *pvError = [pvStderrPipe fileHandleForReading]; 

[pvError waitForDataInBackgroundAndNotify]; 
[[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleDataAvailableNotification 
          object:pvError queue:nil 
          usingBlock:^(NSNotification *note) 
{ 
    NSData *progressData = [pvError availableData]; 
    NSString *progressStr = [[NSString alloc] initWithData:progressData encoding:NSUTF8StringEncoding]; 
    NSLog(@"progress: %@", progressStr); 

    [pvError waitForDataInBackgroundAndNotify]; 
}]; 

一個完全不同的解決方案可能是用「DD」的以下特徵:

如果dd收到一個SIGINFO信號, 當前輸入和輸出塊計數將被寫入 標準錯誤輸出格式相同作爲標準完成 消息。

所以,你可以一個管道連接到 「DD」 的標準錯誤,並呼籲

kill([ddTask processIdentifier], SIGINFO); 

定期。那麼你不需要「PV」,也可能只有一個「DD」任務。

+0

如果我使用'setStandardError',我得到一個非常長的日誌'',觀察者仍然沒有得到更新。我會嘗試第二個建議,但是,我很感激! – Apollo

+0

到目前爲止,'kill'似乎沒有做任何事情,我會檢查文檔。我這樣做:'while([ddTask isRunning]){kill([ddTask processIdentifier],SIGINFO); }'。該文件生成正常,沒有輸出。 – Apollo

+0

@Apollo:我添加了更多在我的測試應用程序中工作的示例代碼。 - 第二個解決方案只是一個想法。你將不得不使用定時器,一個簡單的while循環無法工作,因爲程序控制永遠不會返回到主runloop。 –