简体   繁体   English

在Swift中终止macOS命令行工具的子进程

[英]Terminate subprocesses of macOS command line tool in Swift

I'm writing a macOS command line tool in swift which executes shell commands: 我正在快速编写一个macOS命令行工具来执行shell命令:

let process = Process()
process.launchPath = "/bin/sleep"
process.arguments = ["100"]
process.launch()
process.waitUntilExit()

However, if an interrupt ( CTRL-C ) or a terminate signal gets sent to my program, these shell commands don't get terminated and just continue with their execution. 但是,如果将中断( CTRL-C )或终止信号发送到我的程序,则这些shell命令不会终止,而只会继续执行它们。

Is there a way to automatically terminate them if my program gets terminated unexpectedly? 如果我的程序意外终止,是否可以自动终止它们?

Here is what we did in order to react on interrupt ( CTRL-C ) when using two piped subprocesses. 这是我们的工作,以便在使用两个管道子进程时对中断( CTRL-C )做出反应。

Idea behind : Blocking waitUntilExit() call replaced with async terminationHandler . 背后的思想 :阻止waitUntilExit()调用替换为异步terminationHandler Infinite loop dispatchMain() used to serve dispatch events. 无限循环dispatchMain()用于提供调度事件。 On receiving Interrupt signal we calling interrupt() on subprocesses. 收到Interrupt信号后,我们在子进程上调用interrupt()

Example class which incapsulates subprocess launch and interrupt logic: 封装子流程启动和中断逻辑的示例类:

class AppTester: Builder {

   private var processes: [Process] = [] // Keeps references to launched processes.

   func test(completion: @escaping (Int32) -> Void) {

      let xcodebuildProcess = Process(executableName: "xcodebuild", arguments: ...)
      let xcprettyProcess = Process(executableName: "xcpretty", arguments: ...)

      // Organising pipe between processes. Like `xcodebuild ... | xcpretty` in shell
      let pipe = Pipe()
      xcodebuildProcess.standardOutput = pipe
      xcprettyProcess.standardInput = pipe

      // Assigning `terminationHandler` for needed subprocess.
      processes.append(xcodebuildProcess)
      xcodebuildProcess.terminationHandler = { process in
         completion(process.terminationStatus)
      }

      xcodebuildProcess.launch()
      xcprettyProcess.launch()
      // Note. We should not use blocking `waitUntilExit()` call.
   }

   func interrupt() {
      // Interrupting running processes (if any).
      processes.filter { $0.isRunning }.forEach { $0.interrupt() }
   }
}

Usage (ie main.swift ): 用法(即main.swift ):

let tester = AppTester(...)
tester.test(....) {
   if $0 == EXIT_SUCCESS {
      // Do some other work.
   } else {
      exit($0)
   }
}

// Making Interrupt signal listener.
let source = DispatchSource.makeSignalSource(signal: SIGINT)
source.setEventHandler {
   tester.interrupt() // Will interrupt running processes (if any).
   exit(SIGINT)
}
source.resume()
dispatchMain() // Starting dispatch loop. This function never returns.

Example output in shell: shell中的示例输出:

...
▸ Running script 'Run Script: Verify Sources'
▸ Processing Framework-Info.plist
▸ Running script 'Run Script: Verify Sources'
▸ Linking AppTestability
^C** BUILD INTERRUPTED **

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM