2012-01-17 93 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

相关问题