2010年6月20日日曜日

Automator アクションでの子プロセスの扱い

Automator のアクションで子プロセスを走らせたとき、子プロセスを管理するのはアクション側の責任になります。Automator のワークフローはユーザの操作によって停止することがありますが、その場合、子プロセスを終了させるのは、アクションの実装者の責任です。

AppleScriptObjC で自作アクションを書けますが、ナイーブに do shell script を使って子プロセスを起動させると、停止させる術がありません。NSTask を使って自分で管理するのが良さそうです。

ユーザの停止操作によって、AMAction から継承した stop メソッドが呼ばれますので、そこで動いている子プロセスを停止させます。テンプレートとしては、こんな感じでしょうか。

script my_Automator_Action

  property parent : class "AMBundleAction"
  property NSTask : class "NSTask"
  property NSFileHandle : class "NSFileHandle"
  property task : missing value
 
  -- @override
  on runWithInput_fromAction_error_(input, anAction, errorRef)
    ...
    set command to POSIX Path of "..."
    set args to {}
    set the end of args to "argument 1"
    set the end of args to "argument 2"
    ...
    my runExternalTask(command, args)
    ...
  end runWithInput_fromAction_error_

  -- @override
  on |stop|()
    if task is not missing value then
      task's terminate()
    end if
  end |stop|
 
  -- Instead of 'do shell script', use this method to prepare for abortion by user operations.
  on runExternalTask(command, args)
    set nulldev to NSFileHandle's fileHandleWithNullDevice()
    set task to (NSTask's alloc)'s init()
    task's setLaunchPath_(command)
    task's setArguments_(args)
    task's setStandardInput_(nulldev)
    task's setStandardOutput_(nulldev)
    task's setStandardError_(nulldev)
    task's |launch|()
    task's waitUntilExit()
    task's autorelease()
    set task to missing value
  end runExternalTask

end script

気付いた点をいくつか。

  • NSTask の setArguments: は NSArray を受け取ることになっていますが、以下のようにAppleScript の文字列のリストをそのまま渡しても問題ないようです。ただし、リストの中に数値などが入っていた場合、自動的に文字列に変換してくれず、エラーになります。
  • 標準出力と標準エラー出力を /dev/null へつないでおかないと、コンソールに全部出てしまいます。
  • do shell script は一旦シェルを経由するので、リダイレクトやパイプの指定ができます。同様のことを行うには、NSPipe や NSFileHandle 等を使って自力でやるか、一旦シェルを呼び出すようにします。
  • do shell script は標準出力の内容を戻り値として取得できますが、NSPipe を使って自力で取得する必要があります。

0 件のコメント:

コメントを投稿