How can I execute a terminal command (like grep
) from my Objective-C Cocoa application?
Changes for Swift 3.0:
NSPipe
has been renamedPipe
NSTask
has been renamedProcess
This is based on inkit's Objective-C answer above. He wrote it as a category on NSString
—
For Swift, it becomes an extension of String
.
extension String {
func runAsCommand() -> String {
let pipe = Pipe()
let task = Process()
task.launchPath = "/bin/sh"
task.arguments = ["-c", String(format:"%@", self)]
task.standardOutput = pipe
let file = pipe.fileHandleForReading
task.launch()
if let result = NSString(data: file.readDataToEndOfFile(), encoding: String.Encoding.utf8.rawValue) {
return result as String
}
else {
return "--- Error running command - Unable to initialize string from file data ---"
}
}
}
let input = "echo hello"
let output = input.runAsCommand()
print(output) // prints "hello"
or just:
print("echo hello".runAsCommand()) // prints "hello"
@IBAction func toggleFinderShowAllFiles(_ sender: AnyObject) {
var newSetting = ""
let readDefaultsCommand = "defaults read com.apple.finder AppleShowAllFiles"
let oldSetting = readDefaultsCommand.runAsCommand()
// Note: the Command results are terminated with a newline character
if (oldSetting == "0\n") { newSetting = "1" }
else { newSetting = "0" }
let writeDefaultsCommand = "defaults write com.apple.finder AppleShowAllFiles \(newSetting) ; killall Finder"
_ = writeDefaultsCommand.runAsCommand()
}
Note the Process
result as read from the Pipe
is an NSString
object. It might be an error string and it can also be an empty string, but it should always be an NSString
.
So, as long as it's not nil, the result can cast as a Swift String
and returned.
If for some reason no NSString
at all can be initialized from the file data, the function returns an error message. The function could have been written to return an optional String?
, but that would be awkward to use and wouldn't serve a useful purpose because it's so unlikely for this to occur.