【问题标题】:Terminate subprocesses of macOS command line tool in Swift在 Swift 中终止 macOS 命令行工具的子进程
【发布时间】:2019-03-18 02:14:27
【问题描述】:

我正在用 swift 编写一个执行 shell 命令的 macOS 命令行工具:

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

但是,如果向我的程序发送中断 (CTRL-C) 或终止信号,这些 shell 命令不会终止,而是继续执行。

如果我的程序意外终止,有没有办法自动终止它们?

【问题讨论】:

  • 您找到解决方案了吗?
  • 对于将来发现此问题的任何人,请查看此stackoverflow.com/q/45713819/1187415
  • @sbarow 很遗憾没有
  • @sbarow 我没有让这个工作,是吗?如果是这样,写一个答案。

标签: swift shell command-line-interface command-line-tool


【解决方案1】:

这是我们在使用两个管道子进程时为了对中断 (CTRL-C) 做出反应所做的操作。

背后的想法:阻塞 waitUntilExit() 调用替换为异步 terminationHandler。无限循环dispatchMain() 用于服务调度事件。在接收到Interrupt 信号时,我们在子进程上调用interrupt()

封装子进程启动和中断逻辑的示例类:

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() }
   }
}

用法(即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.

shell 中的示例输出:

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

【讨论】:

  • 谢谢,我今天晚些时候试试!
  • 记得加signal(SIGINT, SIG_IGN)否则源事件处理程序不会被调用。
猜你喜欢
  • 2014-03-25
  • 2011-08-02
  • 2021-07-26
  • 2012-10-05
  • 2020-10-29
  • 1970-01-01
  • 1970-01-01
  • 2019-12-16
  • 2015-04-19
相关资源
最近更新 更多