Running an UNIX program with swift /cocoa? - swift

I have a problem starting a UNIX Programm (Csvmidi) from an cocoa app. It should convert a csv file to a midi file which actually works when i run it through the Terminal.
In Terminal it works fine by putting the URL of the UNIX file, the .csv file and .mid file in a row. Like this:
/Users/...../Csvmid </Users/.../test.csv> /Users/.../Melody.mid -> and automatically the Midi-File changes.
In Windows it worked with an easy code:
Process.Start("Csvmidi.exe", "test.csv Melody.mid");
In swift I tried to make it work with task.Process() but still it won't work. How can I make it work?
let csvmidiURL = "/Users/.../Csvmidi"
let process = Process()
process.executableURL = URL(fileURLWithPath: csvmidiURL)
process.arguments = [" <" , lblURL.stringValue , "> " , lblMidi.stringValue]
process.terminationHandler = { (process) in
print("\ndidFinish: \(!process.isRunning)")
}
do {
try process.run()
} catch {}
Thanks in advance!

Related

How to run Terminal commands from cocoa app?

I have problems running a Terminal command from a Cocoa Application.
The input for the Terminal is real easy: /Users/.../Csvmidi </Users/.../test.csv> /Users/.../Melody.mid
These are three inputs- three actual paths - are just written in a row and seperated by a spac: the first "Csvmidi" runs a Unix Application which converts the test.csv to an actual hearable MIDI file. Through the terminal it works perfectly...
I just don't get it to work via a Cocoa Application.
let process = Process()
process.executableURL = URL(fileURLWithPath: "/bin/zsh/")
process.arguments = [lblCsvmidi.stringValue,"<"+lblURL.stringValue+">",lblMidi.stringValue]
//I saved the URL of the UNIX program, test.csv and Melody.mid in a lable just to be sure.
//lblCsvmidi --> URL of Csvmidi UNIX program
//lblURL --> URL of test.csv
//lblMidi --> URL of Melody.mid
print(lblCsvmidi.stringValue,"<" + lblURL.stringValue + ">",lblMidi.stringValue)
// this print command was only made to check the arguments in the terminal if they would work --> they do
process.terminationHandler = { (process) in
print("\ndidFinish: \(!process.isRunning)")
}
do {
try process.run()
} catch {}
When I run the code it gives me either the error of an 75: unmatched", but actually there isn't a quotation mark in the command - or I get "permission denied" errors.
I tried several bin folders like ( I really tried almost every possible):
- /bin/zsh
- /bin/csh
- /bin/ksh
- ...
What am i doing wrong --> I haven't found information in other Questions here and the Process, NSTask and Bundle informations from Apple haven't helped me so far.
Thanks!!
The following runs without error on my system:
import Cocoa
let process = Process()
process.executableURL = URL(fileURLWithPath: "/bin/zsh/")
var args : [String]!
args = []
args.append("-c")
args.append("open '/Users/xxxx/Desktop/myApp.app/Contents/MacOS/myApp'")
process.arguments = args
process.terminationHandler = { (process) in
print("\ndidFinish: \(!process.isRunning)")
}
do {
try process.run()
} catch {}
let app = NSApplication.shared
app.setActivationPolicy(.regular)
app.activate(ignoringOtherApps:true)
app.run()
The strings after "-c" would depend on what you are trying to accomplish.

Process.run Returns 'The file “<command>” doesn’t exist.'

I'm trying to write a small app to start/stop and display data from a command line "app" someone else wrote. The command executable is installed in '/usr/local/bin'. It outputs status text data to standardOutput while running. I can execute this "command" from the Terminal.app without issue. From swiftUI code I can successfully execute "built-in" commands like ls. However, when (in swiftUI code) I attempt to execute Process.run WITH the new command it throws the exception 'The file “” doesn’t exist.'
Anyone have any ideas? Thanks in advance!
Here's a code snip:
// NOTE: "installedCommand" is just a placeholder for the actual command.
let task = Process()
let connection = Pipe()
let exeUrl = URL(fileURLWithPath: "/usr/local/bin/installedCommand")
//let exeUrl = URL(fileURLWithPath: "/bin/ls") <--works fine
task.executableURL = exeUrl
task.standardOutput = connection
do
{
try task.run()
}
catch
{
print("Error: \(error.localizedDescription)")
return
}

xQuartz display not for shell script launched from Swift Process

I have a simple shell script which launches an X11 app. When I execute this shell script form my login shell / terminal xQuartz starts and I get a display. However the process doesn't get a display for xQuartz when running the script from within swift. Any idea how I can get the display?
Also what is the best way to detect if xQuartz is installed? Checking if xterm exists?
let process = Process()
process.executableURL = URL(fileURLWithPath: "/bin/sh")
let startScriptURL = Bundle.main.url(forResource: "run", withExtension: "sh")
guard let startScriptPath = startScriptURL?.path else {
return
}
process.arguments = [startScriptPath]
do {
try process.run()
} catch let error {
print(error)
}
run.sh:
#!/bin/sh
/opt/X11/bin/xeyes
I figured our how to pass the DISPLAY environment or any environmental variable to Process.
The current environment can be obtained by:
ProcessInfo().environment
So I use now this:
let process = Process()
guard let envDisplay = ProcessInfo().environment["DISPLAY"] else {
print("Please install xQuartz")
return
}
process.environment = ["DISPLAY": envDisplay]
I got the idea from here: Issue launching X11 app via NSTask

How to have access to files via shell commands in mac os x application?

Im new to developing mac applications. (but I have work a lot for ios)
For start I want to have a window on which there is a button. When user click on the button, it should trigger an action that will run some shell commands inside the folder that the user dragged & dropped inside the app before.
For example
User will drag and drop some folder inside my app and the app should create gemfile inside this folder.
Here is my current version where I run the shell and the first command is cd <pathToFolder> but the output is /usr/bin/cd: line 4: cd: file:///Users/klemen/Downloads/ABCAcura/: No such file or directory
Here is my code:
#IBAction func handleCreateGemfileButton(_ sender: NSButton) {
let pipe = Pipe()
let process = Process()
process.launchPath = "/bin/bash"
process.arguments = ["cd", projectURL.absoluteString]
process.standardOutput = pipe
let file = pipe.fileHandleForReading
process.launch()
if let result = NSString(data: file.readDataToEndOfFile(), encoding: String.Encoding.utf8.rawValue) {
print(result as String)
}
else {
print("error")
}
}
Is there some restrictions. How is it possible to request from user that my app will have access to some of his files?
You should use ~/Downloads/ABCAcura instead of file:///..., file:/// is a protocol (URL) not a path to file.

Getting input from stdin in a Cocoa command-line app's sub process

I have a command-line app A, and in A I execute an executable script B, in B I'm expecting an input from stdin.
I wrote a demo, implementing A in Swift, using Foundation's Process api, finding that B, no matter implemented in whatever language, cannot get user input from stdin.
Code:
// `A`'s main.swift
import Foundation
let process = Process()
process.launchPath = PATH_TO_SCRIPT_B
process.launch()
process.waitUntilExit()
// `B`
#!/usr/bin/swift
print("intpu something")
let input = readLine()
print("input: \(input)")
I did not set the process's input since according to the doc:
If this method isn’t used, the standard input is inherited from the process that created the receiver.
UPDATE:
A is an executable package created using Swift Package Manager. I used swift package generate-xcodeproj to generate an Xcode project file. I confirmed that if I run the executable built using swift build or xcodebuild in a shell, the problem with getting input from stdin from B arose. However if I run it directly inside Xcode, by pressing command + R inside Xcode, then it worked. So if I understand the difference between running an executable in a shell and Xcode, I can probably make everything work.
func task() {
print("input here")
let x = input()
print ("inputed:" + x)
}
func input() -> String {
let keyboard = FileHandle.standardInput
let inputData = keyboard.availableData
let strData = String(data: inputData, encoding: .utf8)!
let string = strData.trimmingCharacters(in: .newlines)
return string
}
task()
Hope it helps