6

私が持っているのは、事前に作成された長いシェル スクリプトを実行している NSTask であり、NSProgressIndicator でどれだけ実行されたかを確認したいと考えています。私は多くのことを試しましたが、それを機能させることができないようです。プログレスバーが不確定な場合の使用方法は知っていますが、タスクの進行に合わせてロードしたいです。

スクリプトの実行方法は次のとおりです。

- (IBAction)pressButton:(id)sender {
    NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath:@"/bin/sh"];
    [task setArguments:[NSArray arrayWithObjects:[[NSBundle mainBundle] pathForResource:@"script" ofType:@"sh"], nil]];
    [task launch];
}

そのタスクの進行状況を確認し、それに応じて更新する進行状況バーを配置する必要があります。

4

4 に答える 4

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
于 2012-02-04T21:49:00.353 に答える
1

タスクの進行状況をコールバックまたは中断して、進行状況を確認する方法が必要です。シェルスクリプトについて話している場合は、1つのスクリプトを複数のスクリプトに分割し、スクリプトのセクションが完了すると、進行状況インジケーターを更新できます。他のアプリはこのようなことを行っています。iircSparkleは、解凍コードでカスタムロジックを実行して、進行状況インジケーターを更新できるようにチャンクで解凍しました。同じ効果を達成したい場合は、同様のことを行う必要があります。

于 2012-01-17T02:43:11.177 に答える
0

実行したコマンドの PID (プロセス ID) を取得し、PS (プロセス状態) コマンドと組み合わせて使用​​すると、プロセスの状態を取得できます。コードでその値を使用して、進行状況バーに表示します。

于 2012-01-17T07:42:39.847 に答える
0

スクリプト (nstask) を obj c コントローラーと通信させる簡単な方法は、標準エラーを通信チャネルとして使用することです。これが完璧だとは言いませんが、非常にうまく機能します。タスクを非同期 nstask としてセットアップし、通知を使用して標準入力と標準エラーから別々に読み取る必要があります。必要なら後で載せます。

スクリプトを次のようにラップします。

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

パイプとファイルハンドルを介して標準エラーを処理し、それをアウトレットに接続してテキストでステータスを更新するか、進行状況を表示するために float に変換します。いえ

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

実際の std エラーをキャプチャする必要がある場合は、それをトラップして、私の例のように std out にマージします。これは完璧なアプローチではありませんが、私は頻繁に使用しており、うまく機能しています。

私のiPadではコードがありません。より良い例が必要な場合はお知らせください。

于 2012-02-04T05:28:27.500 に答える