Seeking an exit equivalent in Swift
The exit
function isn't a keyword or built-in in C or Objective-C either. It's a library function. In Swift, you can access it by importing Darwin
:
import Darwin
exit(0)
How to prevent a Command Line Tool from exiting before asynchronous operation completes
I realize this is an old question, but here is the solution I ended on. Using DispatchGroup.
let dispatchGroup = DispatchGroup()
for someItem in items {
dispatchGroup.enter()
doSomeAsyncWork(item: someItem) {
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: DispatchQueue.main) {
exit(EXIT_SUCCESS)
}
dispatchMain()
Swift Command Line Tool utilizing Process() and multithreading crashes after a certain number of execution rounds (~3148)
I have found a fix while researching this bug. It seems that, despite what the documentation claims, Pipe
will not automatically close its reading filehandle.
So if you add a try outputPipe.fileHandleForReading.close()
after reading from it, that will fix the issue.
How do I run a terminal command in a Swift script? (e.g. xcodebuild)
If you don't use command outputs in Swift code, following would be sufficient:
#!/usr/bin/env swift
import Foundation
@discardableResult
func shell(_ args: String...) -> Int32 {
let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = args
task.launch()
task.waitUntilExit()
return task.terminationStatus
}
shell("ls")
shell("xcodebuild", "-workspace", "myApp.xcworkspace")
Updated: for Swift3/Xcode8
Get terminal output after a command swift
NSTask
is class to run another program as a subprocess. You can
capture the program's output, error output, exit status and much more.
Expanding on my answer to xcode 6 swift system() command,
here is a simple utility function to run a command synchronously,
and return the output, error output and exit code (now updated for Swift 2):
func runCommand(cmd : String, args : String...) -> (output: [String], error: [String], exitCode: Int32) {
var output : [String] = []
var error : [String] = []
let task = NSTask()
task.launchPath = cmd
task.arguments = args
let outpipe = NSPipe()
task.standardOutput = outpipe
let errpipe = NSPipe()
task.standardError = errpipe
task.launch()
let outdata = outpipe.fileHandleForReading.readDataToEndOfFile()
if var string = String.fromCString(UnsafePointer(outdata.bytes)) {
string = string.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
output = string.componentsSeparatedByString("\n")
}
let errdata = errpipe.fileHandleForReading.readDataToEndOfFile()
if var string = String.fromCString(UnsafePointer(errdata.bytes)) {
string = string.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
error = string.componentsSeparatedByString("\n")
}
task.waitUntilExit()
let status = task.terminationStatus
return (output, error, status)
}
Sample usage:
let (output, error, status) = runCommand("/usr/bin/git", args: "status")
print("program exited with status \(status)")
if output.count > 0 {
print("program output:")
print(output)
}
if error.count > 0 {
print("error output:")
print(error)
}
Or, if you are only interested in the output, but not in
the error messages or exit code:
let output = runCommand("/usr/bin/git", args: "status").output
Output and error output are returned as an array of strings, one
string for each line.
The first argument to runCommand()
must be the full path to an
executable, such as "/usr/bin/git"
. You can start the program using a shell (which is what system()
also does):
let (output, error, status) = runCommand("/bin/sh", args: "-c", "git status")
The advantage is that the "git" executable is automatically found
via the default search path. The disadvantage is that you have to
quote/escape arguments correctly if they contain spaces or other
characters which have a special meaning in the shell.
Update for Swift 3:
func runCommand(cmd : String, args : String...) -> (output: [String], error: [String], exitCode: Int32) {
var output : [String] = []
var error : [String] = []
let task = Process()
task.launchPath = cmd
task.arguments = args
let outpipe = Pipe()
task.standardOutput = outpipe
let errpipe = Pipe()
task.standardError = errpipe
task.launch()
let outdata = outpipe.fileHandleForReading.readDataToEndOfFile()
if var string = String(data: outdata, encoding: .utf8) {
string = string.trimmingCharacters(in: .newlines)
output = string.components(separatedBy: "\n")
}
let errdata = errpipe.fileHandleForReading.readDataToEndOfFile()
if var string = String(data: errdata, encoding: .utf8) {
string = string.trimmingCharacters(in: .newlines)
error = string.components(separatedBy: "\n")
}
task.waitUntilExit()
let status = task.terminationStatus
return (output, error, status)
}
swift command line tool for git commands, but its no
You are using deprecated methods in your code and there are some other things missing.
First we should set the shell to use
func run(with args: String...){
let task = Process()
task.executableURL = URL(fileURLWithPath: "/bin/zsh")
Then instead of using the deprecated launchPath
we build a string with the full command and set it as the arguments for the task
let arguments = "/usr/bin/git \(args.joined(separator: " "))"
task.arguments = ["-c", arguments]
I also think it is a good idea to handle any errors by checking standard error
let pipe = Pipe()
let errorPipe = Pipe()
task.standardOutput = pipe
task.standardError = errorPipe
Instead of using the deprecated launch
method use run
and read both standard out and standard error
do {
try task.run()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
if !data.isEmpty {
if let output = String(data: data, encoding: .utf8) {
print(output)
}
}
let error = errorPipe.fileHandleForReading.readDataToEndOfFile()
if !error.isEmpty {
if let errorMessage = String(data: error, encoding: .utf8) {
print(errorMessage)
}
}
} catch {
print(error)
}
For a simple command it might be worth having the handling of standard output and standard error in an if/else
so feel free to change that but for more complicated commands dealing for example with multiple files it might produce both output and errors
Related Topics
Difference Between Text("") and Text(Verbatim: "") Initializers in Swiftui
Instance Member Cannot Be Used on Type Class
Iphonex Not Call Prefersstatusbarhidden
Nsdatecomponents Weekofyear Returns Wrong Date
Nsurlerrordomain with Code=-1100
Create an Outlet in Storyboard to an Inherited Property
Wkwebview: How to Handle Blob Url
How to Define Static Constant in a Class in Swift
Sort Array of Objects by Two Properties
How to Link to a 3Rd Party Swift Framework
Show Nsmenu Only on Nsstatusbarbutton Right Click
Convert Hex-Encoded String to String
How to Pass Int.Init to a Function in Swift
Turn for in Loops Local Variables into Mutable Variables
Cannot Convert Value of Type '()' to Specified Type Bool
Can You Override Nsdateformatter 12 VS 24 Hour Time Format Without Using a Custom Dateformat