Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
3
Help us understand the problem. What is going on with this article?
@wheel

[macOS] プログラムからshellコマンドを呼び出す方法

More than 1 year has passed since last update.

プログラムからのshellコマンドを呼び出す方法

macOSプログラムで、プログラム中からshellコマンドを呼び出す方法について説明します。
コマンド実行時の標準入力, 標準出力, 標準エラー出力を、呼び出し側に接続します。

実行環境

  • IDE: xcode 10.0
  • Programming Language: Swift

手順

  1. 入出力(パイプ)の準備
  2. 入出力の接続
  3. 終了時コールバックの接続
  4. 実行および実行終了待ち

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クラスにて実装しています。

3
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
wheel
Steel Wheels Projectというプロジェクトで、主にMac/iOSデバイス向けのオープンソースソフトを開発しています

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
3
Help us understand the problem. What is going on with this article?