2012-01-17 47 views
6

我所擁有的是NSTask運行一個很長的預製shell腳本,我希望NSProgressIndicator檢查完成了多少。我已經嘗試了很多東西,但似乎無法讓它起作用。我知道如何使用它,如果進度條是不確定的,但我希望它隨着任務的進行而加載。如何使用確定的NSProgressIndicator來檢查NSTask的進度? - Cocoa

這裏是我正在運行的腳本:

我需要把一個進度條,在檢查該任務的進度,而它發生,並相應更新。

回答

7

以下是運行unix腳本的異步NSTask示例。在Unix的腳本有echo命令該發回的當前狀態到標準錯誤是這樣的:

echo "working" >&2

這是通過通知中心處理和發送到顯示屏。

要更新確定的進度條,只需發送狀態更新(如「25.0」「26.0」)並轉換爲浮點數併發送到進度條。

注:我經過大量的實驗和使用本網站的許多技巧和其他參考後得到了這個工作。所以我希望這對你有幫助。

以下是聲明:

NSTask *unixTask; 
NSPipe *unixStandardOutputPipe; 
NSPipe *unixStandardErrorPipe; 
NSPipe *unixStandardInputPipe; 
NSFileHandle *fhOutput; 
NSFileHandle *fhError; 
NSData *standardOutputData; 
NSData *standardErrorData; 

這裏是主程序模塊:

- (IBAction)buttonLaunchProgram:(id)sender { 

    [_unixTaskStdOutput setString:@"" ]; 
    [_unixProgressUpdate setStringValue:@""]; 
    [_unixProgressBar startAnimation:sender]; 

    [self runCommand]; 
} 
- (void)runCommand { 

    //setup system pipes and filehandles to process output data 
    unixStandardOutputPipe = [[NSPipe alloc] init]; 
    unixStandardErrorPipe = [[NSPipe alloc] init]; 

    fhOutput = [unixStandardOutputPipe fileHandleForReading]; 
    fhError = [unixStandardErrorPipe fileHandleForReading]; 

    //setup notification alerts 
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 

    [nc addObserver:self selector:@selector(notifiedForStdOutput:) name:NSFileHandleReadCompletionNotification object:fhOutput]; 
    [nc addObserver:self selector:@selector(notifiedForStdError:) name:NSFileHandleReadCompletionNotification object:fhError]; 
    [nc addObserver:self selector:@selector(notifiedForComplete:) name:NSTaskDidTerminateNotification object:unixTask]; 

    NSMutableArray *commandLine = [NSMutableArray new]; 
    [commandLine addObject:@"-c"]; 
    [commandLine addObject:@"/usr/bin/kpu -ca"]; //put your script here 

    unixTask = [[NSTask alloc] init]; 
    [unixTask setLaunchPath:@"/bin/bash"]; 
    [unixTask setArguments:commandLine]; 
    [unixTask setStandardOutput:unixStandardOutputPipe]; 
    [unixTask setStandardError:unixStandardErrorPipe]; 
    [unixTask setStandardInput:[NSPipe pipe]]; 
    [unixTask launch]; 

    //note we are calling the file handle not the pipe 
    [fhOutput readInBackgroundAndNotify]; 
    [fhError readInBackgroundAndNotify]; 
} 
-(void) notifiedForStdOutput: (NSNotification *)notified 
{ 

    NSData * data = [[notified userInfo] valueForKey:NSFileHandleNotificationDataItem]; 
    NSLog(@"standard data ready %ld bytes",data.length); 

    if ([data length]){ 

     NSString * outputString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 
     NSTextStorage *ts = [_unixTaskStdOutput textStorage]; 
     [ts replaceCharactersInRange:NSMakeRange([ts length], 0) 
          withString:outputString]; 
    } 

    if (unixTask != nil) { 

     [fhOutput readInBackgroundAndNotify]; 
    } 

} 
-(void) notifiedForStdError: (NSNotification *)notified 
{ 

    NSData * data = [[notified userInfo] valueForKey:NSFileHandleNotificationDataItem]; 
    NSLog(@"standard error ready %ld bytes",data.length); 

    if ([data length]) { 

     NSString * outputString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 
     [_unixProgressUpdate setStringValue:outputString]; 
    } 

    if (unixTask != nil) { 

     [fhError readInBackgroundAndNotify]; 
    } 

} 
-(void) notifiedForComplete:(NSNotification *)anotification { 

    NSLog(@"task completed or was stopped with exit code %d",[unixTask terminationStatus]); 
    unixTask = nil; 

    [_unixProgressBar stopAnimation:self]; 
    [_unixProgressBar viewDidHide]; 

    if ([unixTask terminationStatus] == 0) { 
     [_unixProgressUpdate setStringValue:@"Success"]; 
    } 
    else { 
     [_unixProgressUpdate setStringValue:@"Terminated with non-zero exit code"]; 
    } 
} 
@end 
+0

感謝這工作。我喜歡你如何放置聲明,有些人不喜歡,它真的讓我感到困惑,因爲我不知道應該如何聲明。再次感謝你在IBAction的開頭 – drewsdunne 2012-02-06 04:24:22

+0

,那些字符串被設置的東西的聲明是什麼 – drewsdunne 2012-02-06 04:45:05

+0

並且是NSMutableArray必需的部分?我不能指向.sh文件並使用/ usr/bin/sh – drewsdunne 2012-02-06 20:48:49

1

你必須有一些方法來回調或中斷任務進度,以告知你已經取得了多少進展。如果你正在談論一個shell腳本,你可以將1個腳本分成多個腳本,並在完成腳本的一部分時更新進度指示器。其他應用程序已經完成了這樣的事情,iirc Sparkle在其解壓縮代碼中執行了一些自定義邏輯以解壓塊,以便它可以更新進度指示器。如果你想達到同樣的效果,你將不得不做類似的事情。

+0

然後我就看看這個感謝 – drewsdunne 2012-01-17 03:06:22

+0

這並不工作,因爲你不能運行在多個NSTasks一個動作 – drewsdunne 2012-01-20 02:39:39

+0

你爲什麼這麼說?你可以啓動一個NSTask,獲得它的結果,啓動一個不同的任務等,我已經編寫了一些應用程序,可以在之前爲各種事情啓動多個任務。唯一的區別是我使用封裝NSTask的類,但除此之外,您應該可以執行相同的操作https://github.com/Machx/Zangetsu/blob/master/Source/CWTask.m – 2012-01-21 16:22:05

0

獲取您運行的命令的PID(進程ID),並將其與PS(進程狀態)命令一起使用,您可以獲取進程的狀態,在代碼中使用該值在進度條中顯示它

+0

我從來沒有聽說過PS和PID,你能告訴我一個例子嗎? – drewsdunne 2012-01-18 00:57:12

+0

ps - 用於獲取任何進程狀態的命令。如果您在終端中使用pid(進程ID,對進程唯一)運行此命令,它將顯示進程狀態爲pid.say,例如:ps -l 185 – 2012-01-19 09:16:14

+0

但我怎麼會這回到我的應用程序,如果它運行在終端 – drewsdunne 2012-01-19 17:59:46

0

讓您的腳本(nstask)與您的obj c控制器進行通信的一種簡單方法是使用標準錯誤作爲通信通道。不是說這是完美的,但工作得很好。您必須將您的任務設置爲異步nstask,並使用通知分別從標準輸入和標準錯誤中讀取。如果您需要,我可以稍後發佈。通過管道和文件句柄,並將其連接

echo "now starting" >&2 
for i in $(ls /tmp) 
do 
echo $i 2>&1 
done 
echo "now ending" >&2 

處理您的標準誤差爲文本狀態更新的插座或將其轉換爲一個浮動的進度顯示器:

包裝你這樣的腳本。即

echo "25.0" >&2. Can be captured and converted. 

如果您需要捕獲真實STD錯誤,那麼它的陷阱和合並它到std出像我的例子。這不是一個完美的方法,但我經常使用它,它運作良好。

在我的iPad上並沒有代碼。讓我知道你是否需要一個更好的例子。

+0

這聽起來像它可以工作,但我不認爲我知道如何做到這一點,因此更好的例子將不勝感激 – drewsdunne 2012-02-04 15:51:33

+0

好的,這裏是上下文。以異步模式運行NSTask。分解出標準輸出和標準錯誤。使用通知中心響應這些頻道上的數據。使用標準錯誤來創建正在運行的文本狀態。基本上,在您的程序運行時,使用如下echo命令將狀態更新發送到標準錯誤:echo「status report」>&2。它將顯示在標準錯誤管道上,然後程序可以將其顯示在屏幕上。或者不是狀態文本,請發送25.0這樣的數字並將它們轉換爲浮動狀態,然後更新確定的進度條。 – CocoaEv 2012-02-04 21:33:51

相關問題