swiftで外部コマンド実行
qiita.com に移動
頭に#!/usr/bin/swift
って書いておくと
chmod a+x hoge.swift ./hoge.swift #swift hoge.swiftでも起動できる
って感じでスクリプトみたいに起動できることを知ったので、勉強がてら手元にあるperlスクリプトをswift翻訳してた。 その際のメモ。
perlの
my $status = system "ls hoge/" #ret-code my $ret = `ls hoge/ ` #output
こういうのをswiftでどう書けばいいんだっけ?と思って調べた。
OSXアプリはほとんど書いたことないので、使ったことなかったけどNSTaskでできるみたい。
task.standardOutput
(NSPipe)で外部コマンドの出力を受け取ることができる。
カレントディレクトリを指定して実行したい場合は、task.currentDirectoryPath
にセットしておけばいい。
func stdOutOfCommand(cmd: String, arguments args: [String], currentDirPath currentDir: String? = nil) -> String { var task: NSTask = NSTask() task.launchPath = cmd task.arguments = args if currentDir != nil { task.currentDirectoryPath = currentDir! } let pipe: NSPipe = NSPipe() task.standardOutput = pipe task.launch() let out: NSData = pipe.fileHandleForReading.readDataToEndOfFile() let outStr: String? = NSString(data: out, encoding: NSUTF8StringEncoding) as? String return outStr == nil ? "" : outStr! } var ret = stdOutOfCommand("/bin/ls", arguments: ["hoge/"])
対話(入力を要求する)形式の場合は、waitForDataInBackgroundAndNotify()
でバックグラウンド待機と通知するようにして、
NSNotificationCenterでNSFileHandleDataAvailableNotification
を受け取るようにする必要がある。
taskの終了まで待ちたい場合はtask.waitUntilExit()
で待つ。
終了ステータスはtask.terminationStatus
で取得できる。
下の例だと、入力の制御はNSFileHandle.fileHandleWithStandardInput()
を使っている。
flushしてなかったり、addObserverのobjectをinPipeにしててはまった...orz
func scriptWithCmd(cmd: String, arguments args: [String], currentDirPath currentDir: String? = nil) -> Int32 { //set task let input: NSFileHandle = NSFileHandle.fileHandleWithStandardInput() let inPipe: NSPipe = NSPipe() let outPipe: NSPipe = NSPipe() let task: NSTask = NSTask() task.launchPath = cmd task.arguments = args if currentDir != nil { task.currentDirectoryPath = currentDir! } task.standardOutput = outPipe task.standardInput = inPipe task.launch() //notification input.waitForDataInBackgroundAndNotify() outPipe.fileHandleForReading.waitForDataInBackgroundAndNotify() NSNotificationCenter.defaultCenter().addObserverForName(NSFileHandleDataAvailableNotification, object: input, queue: nil, usingBlock : { (notification: NSNotification!) in let inData: NSData = input.availableData if inData.length > 0 { inPipe.fileHandleForWriting.writeData(inData) input.waitForDataInBackgroundAndNotify() } else { inPipe.fileHandleForWriting.closeFile() } } ) NSNotificationCenter.defaultCenter().addObserverForName(NSFileHandleDataAvailableNotification, object: outPipe.fileHandleForReading, queue: nil, usingBlock: { (notification: NSNotification!) in let outData: NSData = outPipe.fileHandleForReading.availableData let outStr: NSString = NSString(data: outData, encoding: NSUTF8StringEncoding)! print(outStr) fflush(__stdoutp) outPipe.fileHandleForReading.waitForDataInBackgroundAndNotify() } ) task.waitUntilExit() return task.terminationStatus }
他の言語で外部コマンド呼び出し簡易メモ
以前少し調べてたやつ
Perl
exec()
もあるけど戻ってこないのでsystem()
を使う。
バッククォート便利.
my $status = system "ls hoge/" #ret-code my $ret = `ls hoge/ ` #output
C
以前perlだと遅すぎるので、C/C++でやろうとして色々やっていた時のメモ
- system()
#include <stdlib.h> int ret = system("ls hoge/");
- popen()/pclose()
POSIXだとpopen/pclose
とか使える.
#include <stdio.h> #include <stdlib.h> #include <err.h> #define BUF 256 int main (void) { FILE *fp; char buf[BUF]; char *cmd = "/bin/ls hoge/"; if ((fp=popen(cmd,"r")) == NULL) { err(EXIT_FAILURE, "%s", cmd); } while(fgets(buf, BUF, fp) != NULL) { fputs(buf, stdout); } pclose(fp); return 0; }
- exec*()
popenだと入出力が同時にできないので、pipe()
, dup()
, fork()
, exec*()
あたりを使う必要がある。
C++
Java
ProcessBuilderを使うみたいだが、 シェルが直接解釈するコマンドは直接は実行できない?
参考