Skip to content

Instantly share code, notes, and snippets.

@bnickel
Created January 21, 2015 17:59
Show Gist options
  • Save bnickel/33033929ac1ed9eda0f7 to your computer and use it in GitHub Desktop.
Save bnickel/33033929ac1ed9eda0f7 to your computer and use it in GitHub Desktop.
Provides operators ans wrappers for terse command execution in Swift scripts.
#!/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