Run a .sh file from swift? - swift

I really need to be able to have a .sh file ran from swift! I looked into t and for some reason people want me to run the shell program in the swift file but it doesn't end up working. I'm trying to run one command, really. The shell command does need argument which is going to be passed into from the app, though. The command I'm trying to run is:
chmod a-x /Applications/the input here
I also must be able to take the users input to put the password. How would I input the password?
EDIT
I have already tried:
import Foundation
let task = NSTask();
task.launchPath = "/bin/chmod"
task.arguments = ["a-x /Applications/application.app"]
task.launch()
Does anyone have any tips?

Figured it out! I did not know that the arguments had to be like this:
task.arguments = ["a-x", "/Applications/application.app"]

Related

Why my arguments are being blocked when running a shell command?

I am looking to run a command on command line from my SWIFT app in MAC.
var resString = "open \(app.getLocation()) --args 25 40"
let task = Process()
task.launchPath = "/bin/bash"
task.arguments = ["-c", resString]
task.launch()
print (resString)
when I print the resString on console, I. get the following
open [path to app on my local drive] --args 25 40
which executes normally when I copy paste in the command line. but on the other side, the app is opened but arguments are being ignored.
I also tried this https://stackoverflow.com/a/53725745/12197482 still didn't work, the app is launched but the arguments don't get through
EDIT: Here's the funniest thing that's really frustrating, I created a small script that have a command, which contains
#! /bin/bash
open ./SimulatorDebug.app --args arg1 arg2
I ran it from terminal again, args are passed correctly with no issues. I tried to run at from my app and the same issue happened again, app run play BUT NO ARGS are being passed which is I find really weird.
Try using /bin/sh as executableURL instead:
task.executableURL = URL(fileURLWithPath: "/bin/sh")
That should do it.
You might also want to try using task.run() instead, and waiting for completion:
try task.run()
task.waitUntilExit()
I wrote System to make this much easier. Feel free to use it or copy/paste the code:
https://github.com/eneko/System/blob/master/Sources/System/System.swift#L107
Updated with additional info
Since you are trying to open a macOS app, there is no need to use sh or bash, you can invoke open directly.
To test this, I've written a sample macOS app in Xcode that all it does is display arguments on screen. Then, I proceeded to launch the app from a macOS Playground, as seen in the screenshot:
Here is the code to launch the app with arguments:
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/open")
process.arguments = [
"/Applications/ArgumentTest/ArgumentTest.app",
"-n",
"--args",
"Open",
"From",
"Playground"
]
try process.run()
-n is used to launch an instance of the app, even if the app is already running. Might not be needed in your case.
Hope this helps!

How to use Process Class to execute commands need to enter password in MacOS

I want to execute commands below by Process Class:
let uninstallTask = Process()
uninstallTask.launchPath = "/bin/bash"
//MarkerPro is an app in launchpad
uninstallTask.arguments = ["-c","sudo rm -rf "+"/Applications/MarkerPro.app"]
uninstallTask.launch()
But get errors below:
sudo: no tty present and no askpass program specified
So probably need to enter password before i execute delete command.
I found the solution by using "STPrivilegedTask" which is a tool to get authorization by users.
Using FileManager API I think also a good idea

Create file in Bash Terminal from Swift

I'm trying to use Swift to run a command in the (macOS) terminal. Specifically, to create an empty file. Here's what I have for running in a shell:
import Foundation
func shell(_ args: String...) ->Int32{
let task = Process()
task.launchPath = "/bin/bash"
task.arguments = args
task.launch()
task.waitUntilExit()
return task.terminationStatus
}
Note that I have tried /usr/bin/env/ for the launch path with no good result.
When I call shell("touch /path/to/new/file.txt") it returns an error like:
/bin/bash: touch /Users/my_home_dir/Desktop/file.txt: No such file or directory
127
If I change the launch path it gives a very verbose but unhelpful console message Which reminds me of Python typical output <'class 'demo' at 0x577e040ab4f'>
I've even tried running python in the terminal and creating a file with open().
I am open to any new ways to create files in Swift (which would be great), and any ways to do the above so that it actually works.
Thanks in advance 😉
Calling that command (/bin/bash touch ~/Desktop/touch.txt) directly in Terminal result in the same error.
/usr/bin/touch: cannot execute binary file
If you only want to call "touch" you can set the launch path to /usr/bin/touch and only pass ~/Desktop/touch.txt for the argument.
If you want to call general bash/shell commands you pass -c followed by the shell command joined as a string so that it corresponds to: /bin/bash -c "touch ~/Desktop/touch.txt".

Swift Cocoa - Process() doesn't allow sudo

I have made a cocoa swift application, in which there is a NSTextField. The textfield returns a password, which is then loaded in a Process(), that brings it into a bash command script.
The command is: Echo ${1} | sudo -S echo works
To the process I pass the argument of the password, so it goes instead of the ${1}. When I run the command, the console says that usr/bin/sudo: Operation not permitted.
Is there a way I can use sudo in my bash scripts?
Help would really be appreciated. Thanks.
This is not the correct way to escalate privileges on OS X, particularly because it's terrible security; your passing around the password in cleartext like this is a generally bad practice.
Let me illustrate why this is a problem. Suppose I have this bash script:
#!/bin/bash
sleep 10
echo ${1}
Then, I execute my script, using Process:
import Foundation
let process = Process()
process.launchPath = "/bin/bash"
process.arguments = ["/path/to/foo.sh", "This is a parameter"]
process.launch()
process.waitUntilExit()
Run the script, it outputs "This is a parameter", all seems well. But, while the script is running, in a separate Terminal window, I run this command:
$ ps -ajxww | grep foo.sh
And get the output:
username 85281 85276 85281 0 1 S s006 0:00.00 /bin/bash /path/to/foo.sh This is a parameter
As you can see, the parameter is plainly visible in the process list. If the parameter to that script had been my admin password, I would have just broadcast that password to every interested process on the system.
Anyway, instead of doing things like this, you should create a privileged launchd helper tool, which you can bless using SMJobBless(). The OS will prompt for the password in a secure way, and install your helper tool in a secure location. You can then communicate with your tool via XPC to have it do things as root. See Apple's EvenBetterAuthorizationSample for an idea of how to do this.

Running terminal commands from Cocoa App in Swift failing: "command not found" but works using Command Line Tool in swift

I created an App that exports all of my iMessages into an app I use for journaling. It accesses the chat.db file in Libary/Messages/ to do this. This part works great
I was required to use frameworks and Command Line Tools won't allow you to bundle Frameworks in macOS. I would have preferred to for this all to be a script and avoid Cocoa altogether, but was required to use a full Cocoa application because of the need for bundled Frameworks
I can enter commands like pwd and get a response back. However, when I try to run the terminal commands for the journaling app, it fails with "command not found"
If I run the exact same command from within terminal, or from within a Swift Command Line Tool in Xcode, it works. However, now that I'm using an actual Cocoa app it won't work.
Here is an example of my code:
let pipe = Pipe()
let task = Process()
task.launchPath = "/bin/sh"
task.arguments = ["-c", String(format:"%#", "dayone2 new 'Hello'")]
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 ---"
}
and the response:
/bin/sh: dayone2: command not found
Add "--login" as the first task argument:
task.arguments = ["--login", "-c", "dayone2 new 'Hello'"]
and that should fix your error.
Explanation:
When you run Terminal the shell starts up as a login shell, from man sh:
When bash is invoked as an interactive login shell, or as a
non-inter-active shell with the --login option, it first reads and executes commands from the file /etc/profile, if that file exists. After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from the first one that exists and is readable.
Among other things the commands in these files typically set the $PATH environment variable, which defines the search path the shell uses to locate a command.
When you run your command line tool in the Terminal it inherits this environment variable and in turn passes it on to the shell it invokes to run your dayone2 command.
When you run a GUI app there is no underlying shell and the $PATH variable is set to the system default. Your error "command not found" indicates that your dayone2 command is not on the default path.
HTH