Swift NStask function - swift

I'm a complete swift noob. Using this code in xcode I get the result I need. I created a command line binary "menubar" that takes several arguments. I normally run it in the terminal "/bin/menubar getip", "/bin/menubar/getuser". I want to create a function based on the following working code.
import Cocoa
import Foundation
var task:NSTask = NSTask()
var pipe:NSPipe = NSPipe()
task.launchPath = "/bin/menubar"
task.arguments = ["getip"]
task.standardOutput = pipe
task.launch()
var handle = pipe.fileHandleForReading
var data = handle.readDataToEndOfFile()
var result_s = NSString(data: data, encoding: NSUTF8StringEncoding)
print(result_s)
I want to convert it to a function.
func commmand (argument: String) -> String
{
let task:NSTask = NSTask()
let pipe:NSPipe = NSPipe()
task.launchPath = "/bin/menubar"
task.arguments = ["argument"]
task.standardOutput = pipe
task.launch()
let handle = pipe.fileHandleForReading
let data = handle.readDataToEndOfFile()
let result_s = NSString(data: data, encoding: NSUTF8StringEncoding)
return result_s
}
commmand getip

Try this:
func commmand(argument: String) -> String
{
let task:NSTask = NSTask()
let pipe:NSPipe = NSPipe()
task.launchPath = "/bin/menubar"
task.arguments = [argument]
task.standardOutput = pipe
task.launch()
let handle = pipe.fileHandleForReading
let data = handle.readDataToEndOfFile()
let result_s = String(data: data, encoding: NSUTF8StringEncoding)!
return result_s
}
print(commmand("getip"))

Related

How to upload results to label in shell from swift code?

Everything is working fine, but I don't understand why the wrong label "loading" doesn't update the percentage. while i print the output value is continuously changing
do {
#discardableResult
func shell(_ args: String...) -> (String?, Int32) {
let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = args
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)
task.waitUntilExit()
return (output, task.terminationStatus)
}
shell(....)
let strURL = "http://download.com/load.zip"
let url = URL(string: strURL)
FileDownloader(url! as NSObject).download(url: url!)
var result = ""
func checkdl() {
let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
let fileURL = dir!.appendingPathComponent("load.txt")
result = try! String(contentsOf: fileURL, encoding: .utf8)
print(result)
if result.contains("100.0") {
try! "".write(to: fileURL, atomically: true, encoding: String.Encoding.utf8)
print("download done")
} else {
sleep(2)
loading.stringValue = result+"%"
checkdl()
}
}
checkdl()
shell(....)
}
Everything is working fine, but I don't understand why the wrong label "loading" doesn't update the percentage. while i print the output value is continuously changing

Redirect the output of Terminal command to TextView

I want to execute a Terminal command in my Application and redirect the Terminal output of this command to a TextView (content_scroller). If I run the Application with Apple+R from within Xcode the Progress of this Terminal command is refreshed as it should. But ... If I started the Application the normal way only the first line of terminal output is shown but there is no refresh/new lines anymore. But why? Is there a way to loop the request of the actual output? Here is mit Swift 5 Code:
func syncShellExec(path: String, args: [String] = []) {
let process = Process()
process.launchPath = "/bin/bash"
process.arguments = [path] + args
let outputPipe = Pipe()
let filelHandler = outputPipe.fileHandleForReading
process.standardOutput = outputPipe
process.launch()
filelHandler.readabilityHandler = { pipe in
let data = pipe.availableData
if let line = String(data: data, encoding: .utf8) {
DispatchQueue.main.sync {
self.content_scroller.string += line
self.content_scroller.scrollToEndOfDocument(nil)
}
}
process.waitUntilExit()
filelHandler.readabilityHandler = nil
}
Should be able to direct output straight to text view if I understand your question correctly. Something like the following outputs an error (I didn't test it.)
import Cocoa
func syncShellExec(path: String, args: [String] = []) {
var status : Int32
var dataRead : Data
var stringRead :String?
let process = Process()
process.launchPath = "/bin/bash"
process.arguments = [path] + args
let outputPipe = Pipe()
let txtView = NSTextView()
let fileHandler = outputPipe.fileHandleForReading
process.standardOutput = outputPipe
process.launch()
process.waitUntilExit()
status = process.terminationStatus
dataRead = fileHandler.readDataToEndOfFile()
stringRead = String.init(data: dataRead, encoding: String.Encoding.utf8)
if (status != 0) {
txtView.string.append("Terminated with error.\n")
txtView.string.append(stringRead!)
}
}

How to run shell command in Swift?

I want to run shell command using NSButton in Swift. The IBAaction and execute function I use cannot work, pls help me, thanks!
#IBAction func Openfolder(_ sender: NSButton) {
_ = execute(command:"open " + "~/Downloads/")
}
func execute(command: String) -> String {
var arguments:[String] = []
arguments.append("-c")
arguments.append( command )
let task = Process()
task.launchPath = "/bin/sh"
task.arguments = arguments
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.launch()
task.waitUntilExit()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
return(NSString(data: data, encoding: String.Encoding.utf8.rawValue)! as String)
}

Swift pass commands into a jar file

I have a java program running in the background of my swift program. The java program can read user input in a command line. How can I pass "commands", or text line into the jar after it has been launched?
Just like the way the program down below read from the java program, how can I then "reply" to it?
let b = Bundle.main
let path = b.path(forResource: "myjar", ofType: "jar")!
NSLog("%#", "jar path : \(path)")
task.launchPath = "/usr/bin/java"
task.arguments = ["-jar", path]
let pipe = Pipe()
task.standardOutput = pipe
let errorPipe = Pipe()
task.standardError = errorPipe
task.launch()
let outHandle = pipe.fileHandleForReading
outHandle.waitForDataInBackgroundAndNotify()
var progressObserver : NSObjectProtocol!
progressObserver = NotificationCenter.default.addObserver(
forName: NSNotification.Name.NSFileHandleDataAvailable,
object: outHandle, queue: nil){
notification -> Void in
let data = outHandle.availableData
if data.count > 0 {
if let str = String(data: data, encoding: String.Encoding.utf8) {
NSLog("%#", str)
}
outHandle.waitForDataInBackgroundAndNotify()
} else { NotificationCenter.default.removeObserver(progressObserver)
}
}
I then tried the following without luck:
pipe.fileHandleForWriting.write("text to send to java program".data(using: String.Encoding.utf8)
You're pretty close. If you just added the code pipe.fileHandleForWriting.write("text to send to java program"... that's not going to work because that pipe is the one you assigned to standardOutput.
You need to create yet another Pipe object, assign it to standardInput, and write to that:
...
>> let inputPipe = Pipe()
>> task.standardInput = inputPipe
task.launch()
>> inputPipe.fileHandleForWriting.write("text to send to java program".data(using: String.Encoding.utf8)
...

Osx NSTask / Process, combine standardError and standardOutput in one string while also having both separated

I'm trying to get a separated standardError output and standardOutput string but I also need the full string. Following code will explain my case
combined/full string:
let task = Process()
task.launchPath = "/usr/sbin/killall"
task.arguments = ["Dock"]
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.launch()
task.waitUntilExit()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let fulloutput: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue) as! String
print(fulloutput)
Separated strings:
let task = Process()
task.launchPath = "/usr/sbin/killall"
task.arguments = ["Dock"]
let pipe = Pipe()
let errorpipe = Pipe()
task.standardOutput = pipe
task.standardError = errorpipe
task.launch()
task.waitUntilExit()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let errordata = pipe.fileHandleForReading.readDataToEndOfFile()
let output: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue) as! String
let erroroutput: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue) as! String
print(output)
print(erroroutput)
How do I combine these 2 so I get an output like this without running the command twice?:
print(output)
print(erroroutput)
print(fulloutput)
Thanks!