プログラムからのshellコマンドを呼び出す方法
macOSプログラムで、プログラム中からshellコマンドを呼び出す方法について説明します。
コマンド実行時の標準入力, 標準出力, 標準エラー出力を、呼び出し側に接続します。
実行環境
- IDE: xcode 10.0
- Programming Language: Swift
手順
- 入出力(パイプ)の準備
- 入出力の接続
- 終了時コールバックの接続
- 実行および実行終了待ち
1. 入出力(パイプ)の準備
Pipeオブジェクトを生成し、読み出しあるいは書き込みハンドラを提議します。
プログラム中のcons
オブジェクトは、シェルコマンドにデータを入力し、出力を受け取るオブジェクトです。シェルコマンドからのcons
オブジェクトアクセスは、アプリケーションに対して非同期である点にご注意ください。
let inpipe = Pipe()
let outpipe = Pipe()
let errpipe = Pipe()
inpipe.fileHandleForWriting.writeabilityHandler = {
(filehandle: FileHandle) -> Void in
if let str = cons.scan() {
if let data = str.data(using: .utf8) {
filehandle.write(data)
} else {
NSLog("Error encoding data: \(str)")
}
}
}
outpipe.fileHandleForReading.readabilityHandler = {
(_ handle: FileHandle) -> Void in
if let str = String(data: handle.availableData, encoding: String.Encoding.utf8) {
cons.print(string: str)
} else {
NSLog("Error decoding data: \(handle.availableData)")
}
}
errpipe.fileHandleForReading.readabilityHandler = {
(_ handle: FileHandle) -> Void in
if let str = String(data: handle.availableData, encoding: String.Encoding.utf8) {
cons.error(string: str)
} else {
NSLog("Error decoding data: \(handle.availableData)")
}
}
2. 入出力の接続
上で生成したパイプをshellコマンド実行用プロセスに割り付けます。
let process = Process()
process.launchPath = "/bin/sh"
process.arguments = ["-c", <シェルコマンドと引数の文字列>]
process.standardInput = inpipe
process.standardOutput = outpipe
FileHandle.standardOutput = errpipe
3. 終了時コールバックの接続
終了時コールバックを設定し、コールバックが呼び出された時に、Read/Writeハンドラをnilでクリアするのがポイントです。これを忘れるとシェルコマンド実行終了あと、ハンドラが何度も呼び出され、プログラムがハングに陥ります(高負荷状態になります)。
process.terminationHandler = {
(process: Process) -> Void in
if let handler = termhdl {
handler(process.terminationStatus) // コールバック関数呼び出し
}
if let inpipe = process.standardInput as? Pipe {
inpipe.fileHandleForWriting.writeabilityHandler = nil
}
if let outpipe = process.standardOutput as? Pipe {
outpipe.fileHandleForReading.readabilityHandler = nil
}
if let errpipe = process.standardError as? Pipe {
errpipe.fileHandleForReading.readabilityHandler = nil
}
}
4. 実行および実行終了待ち
プロセスを実行開始します。必要に応じてプロセスの実行終了を待ちます。
process.launch()
process.waitUntilExit()
参照
本ドキュメントで説明した実装は、自作のフレームワークCoconut Frameworkの、CNShelクラスにて実装しています。