Processってなに?
ちょっと前までNSTaskと呼ばれていた、主にコマンドラインツールを実行してデータのやり取りを行うためのクラスです。
SwiftでできることはSwiftでやってしまってもいいのですが、swiftcを呼びたいとかそういう時に使います。
の4回目。
振り返り
前回出力を得るカスタムオペレーションを追加して下のようになりました。
Process() <<< "/bin/echo" <<< ["Hello, World."] >>> { output in
output.string.map { print($0) }
}
完璧です。
ところで皆さん。コマンドラインにおけるパイプラインってご存知ですか?
そうです。|
です。
これを今までの成果を利用して書いてみましょう。
let echo = Process() <<< "/bin/echo" <<< ["Hello, World.\nこんにちは世界。"]
let grep = Process() <<< "/usr/bin/grep" <<< ["Hello"]
let pipe = Pipe()
echo.standardOutput = pipe
grep.standardInput = pipe
echo.launch()
grep >>> { output in
output.string.map { print($0) }
}
またPipe
とlaunch()
が現れた!!!
パイプも簡単に
パイプを行うカスタムオペレーションを追加します。
func | (lhs: Process, rhs: Process) -> Process {
let pipe = Pipe()
lhs.standardOutput = pipe
rhs.standardInput = pipe
lhs.launch()
return rhs
}
ふたつのProcess
をパイプし新しいProcess
を取り出します。
右側が返ってくるだけですが、中身が見えているからそう思うだけです!
ふたつのProcess
がパイプされて新しいProcess
が生まれたのです。
使用法
Process() <<< "/bin/echo" <<< ["Hello, World.\nこんにちは世界。"]
| Process() <<< "/usr/bin/grep" <<< ["Hello"]
>>> { output in
output.string.map { print($0) }
}
短い! わかりやすい!
まとめ
今まで作ったカスタムな何かをまとめたものがこちらです。
/// 出力を簡単に扱うための補助的な型。FileHandleを隠蔽する。
struct Output {
private let fileHandle: FileHandle
init(fileHandle: FileHandle) {
self.fileHandle = fileHandle
}
var data: Data {
return fileHandle.readDataToEndOfFile()
}
var string: String? {
return String(data: data, encoding: .utf8)
}
var lines: [String] {
return string?.components(separatedBy: "\n") ?? []
}
}
precedencegroup ArgumentPrecedence {
associativity: left
higherThan: AdditionPrecedence
}
infix operator <<< : ArgumentPrecedence
/// Processにexecutable pathを設定する。
func <<< (lhs: Process, rhs: String) -> Process {
lhs.executableURL = URL(fileURLWithPath: rhs)
return lhs
}
/// Processに引数を設定する。
func <<< (lhs: Process, rhs: [String]) -> Process {
lhs.arguments = rhs
return lhs
}
/// Processをパイプする。
func | (lhs: Process, rhs: Process) -> Process {
let pipe = Pipe()
lhs.standardOutput = pipe
rhs.standardInput = pipe
lhs.launch()
return rhs
}
precedencegroup RedirectPrecedence {
associativity: left
lowerThan: LogicalDisjunctionPrecedence
}
infix operator >>> : RedirectPrecedence
/// Processの出力をOutput型で受け取り加工などができる。
/// ジェネリクスを利用しているのでどのような型にでも変換して返せる。
func >>> <T>(lhs: Process, rhs: (Output) -> T) -> T {
let pipe = Pipe()
lhs.standardOutput = pipe
lhs.launch()
return rhs(Output(fileHandle: pipe.fileHandleForReading))
}
Process
クラスには直接手を加えず、カスタムオペレーションと補助的な型を追加することで、Process
クラスをとてもわかりやすく、そして短い記述で使用できるようになりました。
オペレータの右側を関数にすることを思いついた時は思わず飛び上がりそうになりました。
こういうのを考えてるととても楽しくなりますね。
それではみなさん
let string = Process() <<< "/bin/echo" <<< ["Hello, World.\nこんにちは世界。"]
| Process() <<< "/usr/bin/grep" <<< [","]
| Process() <<< "/usr/bin/sed" <<< ["-e", "s/Hel/We/"]
| Process() <<< "/usr/bin/sed" <<< ["-e", "s/,/ to New/"]
| Process() <<< "/usr/bin/sed" <<< ["-e", "s/o/come/"]
>>> { $0.string }
string.map { print($0) }