Created
January 21, 2015 17:59
-
-
Save bnickel/33033929ac1ed9eda0f7 to your computer and use it in GitHub Desktop.
Provides operators ans wrappers for terse command execution in Swift scripts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/swift | |
// TL;DR: Skip to line 183. | |
// To run as a script, run with /usr/bin/swift or chmod +x. | |
// To run as a playground, comment out the first line and `mkdir -p "~/Documents/Shared Playground Data"`. | |
import Foundation | |
let stdin = NSFileHandle.fileHandleWithStandardInput() | |
let stdout = NSFileHandle.fileHandleWithStandardOutput() | |
let stderr = NSFileHandle.fileHandleWithStandardError() | |
let nullDevice = NSFileHandle.fileHandleWithNullDevice() | |
protocol Task { | |
var standardInput:AnyObject {get set} | |
var standardOutput:AnyObject {get set} | |
var standardError:AnyObject {get set} | |
func run() -> Int | |
func runAsync(#queue:dispatch_queue_t, callback:Int -> ()) | |
} | |
extension NSTask : Task { | |
func run() -> Int { | |
launch() | |
waitUntilExit() | |
return Int(terminationStatus) | |
} | |
func runAsync(#queue: dispatch_queue_t, callback: Int -> ()) { | |
launch() | |
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), { () -> Void in | |
self.waitUntilExit() | |
dispatch_async(queue, { () -> Void in | |
callback(Int(self.terminationStatus)) | |
}) | |
}) | |
} | |
} | |
class CombinedTask : Task { | |
private var task1:Task | |
private var task2:Task | |
init(var task1:Task, var task2:Task) { | |
let pipe = NSPipe() | |
task1.standardOutput = pipe | |
task2.standardInput = pipe | |
self.task1 = task1 | |
self.task2 = task2 | |
} | |
func run() -> Int { | |
var code = 1 | |
var group = dispatch_group_create() | |
dispatch_group_enter(group) | |
runAsync(queue: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) { status -> () in | |
code = status | |
dispatch_group_leave(group) | |
} | |
dispatch_group_wait(group, DISPATCH_TIME_FOREVER) | |
return code | |
} | |
func runAsync(#queue: dispatch_queue_t, callback: Int -> ()) { | |
let backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0) | |
var code1 = 1 | |
var code2 = 1 | |
var group = dispatch_group_create() | |
dispatch_group_enter(group) | |
task1.runAsync(queue: backgroundQueue) { status -> () in | |
code1 = status | |
dispatch_group_leave(group) | |
} | |
dispatch_group_enter(group) | |
task2.runAsync(queue: backgroundQueue) { status -> () in | |
code2 = status | |
dispatch_group_leave(group) | |
} | |
dispatch_group_notify(group, queue) { () -> Void in | |
callback(code1 != 0 ? code1 : code2) | |
} | |
} | |
var standardInput:AnyObject { | |
get { return task1.standardInput } | |
set (value) { task1.standardInput = value } | |
} | |
var standardOutput:AnyObject { | |
get { return task2.standardOutput } | |
set (value) { task2.standardOutput = value } | |
} | |
var standardError:AnyObject { | |
get { return task2.standardError } | |
set (value) { task2.standardError = value } | |
} | |
} | |
func task(launchPath:String, arguments:String...) -> Task { | |
let task = NSTask() | |
task.launchPath = launchPath | |
task.arguments = arguments | |
return task | |
} | |
func |(lhs:Task, rhs:Task) -> Task { | |
return CombinedTask(task1: lhs, task2: rhs) | |
} | |
func <(var lhs:Task, rhs:AnyObject) -> Task { | |
lhs.standardInput = rhs | |
return lhs | |
} | |
func >(var lhs:Task, rhs:AnyObject) -> Task { | |
lhs.standardOutput = rhs | |
return lhs | |
} | |
func <(var lhs:Task, var rhs:String) -> Task { | |
rhs = rhs.stringByStandardizingPath | |
if let target = NSFileHandle(forReadingAtPath: rhs) { | |
lhs.standardInput = target | |
} else { | |
let string:NSString = "ERROR: Could not open \(rhs) for reading.\n" | |
stderr.writeData(string.dataUsingEncoding(NSUTF8StringEncoding)!) | |
} | |
return lhs | |
} | |
func >(var lhs:Task, var rhs:String) -> Task { | |
rhs = rhs.stringByStandardizingPath | |
NSFileManager.defaultManager().createFileAtPath(rhs, contents: nil, attributes: nil) | |
if let target = NSFileHandle(forWritingAtPath: rhs) { | |
lhs.standardOutput = target | |
} else { | |
let string:NSString = "ERROR: Could not open \(rhs) for writing.\n" | |
stderr.writeData(string.dataUsingEncoding(NSUTF8StringEncoding)!) | |
} | |
return lhs | |
} | |
func >>(var lhs:Task, var rhs:String) -> Task { | |
rhs = rhs.stringByStandardizingPath | |
let manager = NSFileManager.defaultManager() | |
if !manager.fileExistsAtPath(rhs) { | |
manager.createFileAtPath(rhs, contents: nil, attributes: nil) | |
} | |
if let target = NSFileHandle(forWritingAtPath: rhs) { | |
target.seekToEndOfFile() | |
lhs.standardOutput = target | |
} else { | |
let string:NSString = "ERROR: Could not open \(rhs) for writing.\n" | |
stderr.writeData(string.dataUsingEncoding(NSUTF8StringEncoding)!) | |
} | |
return lhs | |
} | |
let reportFile = "~/Documents/Shared Playground Data/report" | |
(task("/bin/mkdir", "-p", "~/Documents/Shared Playground Data".stringByStandardizingPath)).run() | |
(task("/bin/date") > reportFile).run() | |
(task("/bin/echo", "-n" , "Number of lines in /etc/passwd using cat: ") >> reportFile).run() | |
(task("/bin/cat", "/etc/passwd") | task("/usr/bin/wc", "-l") >> reportFile).run() | |
(task("/bin/echo", "-n" , "Number of lines in /etc/passwd using file: ") >> reportFile).run() | |
((task("/usr/bin/wc", "-l") < "/etc/passwd") >> reportFile).run() | |
(task("/bin/echo", "Generated the following report at ", reportFile)).run() | |
((task("/bin/cat") < reportFile) > stdout).run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment