Bash command not found using swift - swift

I created a swift function which runs command in bash which is :
func getConnectedDevices(lblOut: NSTextView)
{
let pipe = Pipe()
let process = Process()
process.launchPath = "/bin/bash"
process.arguments = ["--login", "-c", "mobiledevice get_device_prop DeviceName"]
process.standardOutput = pipe
let fileHandle = pipe.fileHandleForReading
process.launch()
lblOut.string += "\n" + String(data: fileHandle.readDataToEndOfFile(), encoding: .utf8)!
}// Gets all connected iOS Devices
This function works if I just use mobiledevice in command but when I pass proper command to obtain list it gives me error that command not found. I am not very experienced in swift.

The issue resolved by itself. I don't really know how as I didn't change anything at all.

Related

Open an http tunnel through a shell script programmatically in swift

I am trying to open either an Ngrok or Localtunnel as part of my swift program by using shell scripts. The problem is as neither shell script has a return, my main thread hangs. I could put it in a side thread that's permanently open, but I need to extract the url that gets outputted. Any ideas?
For reference this is how I am executing shell commands in swift.
func shell(_ command: String) -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.arguments = ["-c", command]
task.launchPath = "/bin/zsh"
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
followed by either
shell("ngrok http 3000")
or
shell("lt --port 9726")

Running shell commands in a swift script

I am looking for a solutuion to run shell commands in a Swift script.
Here is my code:
func shellEnv(_ command: String) -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.arguments = ["-c", command]
task.launchPath = "/bin/zsh"
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
It works with built-in commands but it cannot deal with commands like "brew", "node" and other commands that were installed manually.
So how can I solve it?
You need to set the PATH environment variable for the task. This has been set in your terminal, which is why you are able to do brew and node and other cool things directly, without specifying their full path.
You can see what this is set to in your terminal by doing:
echo $PATH
and it will print something like (for me it has a bunch more things. This is only an excerpt):
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
If you copy and paste the entire output of echo $PATH, and put it into the environment property of task, then you will be able to use the same commands in your swift script as in your terminal.
task.environment = ["PATH": "<paste the output here>"]
As Alexander said in the comments, another way is to add the l option. Here is a MCVE:
#!/usr/bin/swift
// main.swift
import AppKit
func shellEnv(_ command: String) -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.arguments = ["-cl", command]
task.launchPath = "/bin/zsh"
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
print(shellEnv("brew list")) // assuming you have brew
To run, chmod +x main.swift then ./main.swift, and you will see all your homebrew packages listed.

Executing python from swift via bash: "can't open file...[Errno 1] Operation not permitted"

I am learning how to make apps for mac, and I am starting with an app to manage my many discord bots. Essentially, the goal is to have many switches to turn bots on and off, which requires executing the python files for the bots, written using discord.py. I read about a PythonKit module for swift, but when I tried to run a discord bot from the files using that, the build continuously failed, so I decided to use the bash shell to excecute the python. Here is my swift code for using bash shell commands:
func shell(_ command: String) -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.arguments = ["-c", command]
task.launchPath = "/bin/zsh"
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}func shell(_ command: String) -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.arguments = ["-c", command]
task.launchPath = "/bin/zsh"
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
I then called shell("/usr/local/bin/python3.9 path/to/file.py"), and made it print the output to the console. This was the output:
/usr/local/bin/python3.9: can't open file 'path/to/file.py': [Errno 1] Operation not permitted. I am running this from both AppCode and Xcode, and made sure to give both of those apps full disk access, as well as giving terminal full disk access to be sure. In addition, I tried running /usr/local/bin/python3.9 path/to/file.pyin my terminal, and it works fine. What is happening here? Why can swift not open this file in the bash shell while I can? What do I do to fix it? Let me know if you need more info(if you think PythonKit is the answer, I'll send the build error info from that to debug that process)
Thanks!
Yonatan Vainer was right, turns out sandboxing was on in the entitlements file, so I had to set it to off, which fixed it.

How to run terminal command in swift from any directory?

I'm trying to creating a macOS application that that involves allowing the user to run terminal commands. I am able to run a command, but it runs from a directory inside my app, as far as I can tell. Running pwd returns /Users/<me>/Library/Containers/<My app's bundle identifier>/Data.
How can I chose what directory the command runs from?
I'm also looking for a way to get cd to work, but if I can chose what directory to run the terminal command from, I can handle cd manually.
Here is the code that I'm currently using to run terminal commands:
func shell(_ command: String) -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.arguments = ["-c", command]
task.launchPath = "/bin/zsh"
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
I'm using Xcode 12 on Big Sur.
Thanks!
There is a deprecated property currentDirectoryPath on Process.
On the assumption you won't want to use a deprecated property, after reading its documentation head over to the FileManager and look at is provisions for managing the current directory and their implications.
Or just use cd as you've considered – you are launching a shell (zsh) with a shell command line as an argument. A command line can contain multiple commands separated by semicolons so you can prepend a cd to your command value.
The latter approach avoids changing your current process' current directory.
HTH
To add to CRD's answer, if using the cd approach, you may also consider separating your commands using && to wait for the previous commands to complete successfully before proceeding to the next command that depends on it.
Try the command you wish to run in the terminal and see if it completes as expected
Eg: /bin/bash -c "cd /source/code/ && git pull && swift build"
If everything works as expected you can go ahead and use it in your swift code as so:
shell("cd /source/code/ && git pull && swift build")
On the topic of deprecations, you may want to replace
launchPath with executableURL
and
launch() with run()
Sample implementation with updated code:
#discardableResult
func shell(_ args: String...) -> Int32 {
let task = Foundation.Process()
task.executableURL = URL(fileURLWithPath: "/bin/bash")
task.arguments = ["-c"]
task.arguments = task.arguments! + args
//Set environment variables
var environment = ProcessInfo.processInfo.environment
environment["PATH"]="/usr/bin/swift"
//environment["CREDENTIALS"] = "/path/to/credentials"
task.environment = environment
let outputPipe = Pipe()
let errorPipe = Pipe()
task.standardOutput = outputPipe
task.standardError = errorPipe
do {
try task.run()
} catch {
// handle errors
print("Error: \(error.localizedDescription)")
}
task.waitUntilExit()
let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile()
let output = String(decoding: outputData, as: UTF8.self)
let error = String(decoding: errorData, as: UTF8.self)
//Log or return output as desired
print(output)
print("Ran into error while running: \(error)")
return task.terminationStatus
}

Swift + terminal

I'm looking for a way to run terminal commands from in Swift (macOS). I came accross this post, but I can't seem to get any of the solutions to work. I am trying to shut down my mac from my app as you can do from terminal (osascript -e 'tell app "loginwindow" to «event aevtrsdn»'), but whenever I do it, I get error: Couldn't posix_spawn: error 13.
I am using this code:
func shell(launchPath: String, arguments: [String] = []) -> (String? , Int32) {
let task = Process()
task.launchPath = launchPath
task.arguments = arguments
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)
}
and I call it from this:
let z = shell(launchPath: "/usr/bin/osascript", arguments: ["-e", "\'tell app \"loginwindow\" to «event aevtrsdn»\'"])
Any help?
Your code is correct, but you must not enclose the second argument
in single-quotes:
let z = shell(launchPath: "/usr/bin/osascript", arguments: ["-e", "tell app \"loginwindow\" to «event aevtrsdn»"])
That is only necessary when executing a program from the shell.
Process passes the given arguments directly to the spawned executable,
without interpretation by a shell.