swift process standart input pipe - swift

I want to create an encrypted .dmg on macOS using swift
I haven't found an implementation for hdiutil, so I'm forced to use terminal commands for that.
Unfortunately, I don't know how to work with pipes (which should be used regarding apples documentation)
The command i want to trigger is the following:
printf 'password' | hdiutil create -encryption AES-256 -volname backupname -stdinpass -size 1GB backupname.dmg
The code, I've tried is the following (nothing worked):
func shell(_ args: String...) -> Int32 {
let task = Process()
let inputpipe = Pipe()
task.standardInput = inputpipe
//1. try: inputpipe.fileHandleForReading.write("password".data(using: .utf8)!)
//2. try: inputpipe.fileHandleForWriting.write("password".data(using: .utf8)!)
/*3. try: inputpipe.fileHandleForWriting.writeabilityHandler = {
pipe in
let inputString = "password"
let data = inputString.data(using: .utf8)!
pipe.write(data)
}*/
task.launchPath = "/usr/bin/env"
task.arguments = args
task.launch()
task.waitUntilExit()
return task.terminationStatus
}
print(shell("hdiutil", "create", "-encryption", "AES-256", "-volname", "backupname", "-stdinpass", "-size", "1MB", "~/Documents/backupname.dmg"))
How do i correctly provide the password using Process()

Related

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.

macOS Swift Launch Location Not Accessible

I want to execute the following shell command from my macOS app:
/Users/macuser/Desktop/videos/ffmpeg -i /Users/macuser/Desktop/videos/vid1.mov -vf fps=1/1 /Users/macuser/Desktop/videos/images/out%0d4.jpg
The method I'm using to do this is:
func shell(_ launchPath: String, _ arguments: [String]) -> String?
{
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
}
I am passing the arguments to the method like this:
let command = ffmpegLocation
let arguments = [
"-i",
videoLocation,
"-vf",
"fps=1/1",
"\(outputLocation)/out%0d4.jpg"
]
resultsField.stringValue = shell(command,arguments)!
What I get is "launch path is not accessible". I have verified the command does work in Terminal. Do I need to use a different approach?

swift command line tool for git commands, but its no

I would like to create a tool for git commands using swift.
I'm getting git error usage: git [--version] [--help] [-C ]
[-c =].
Is there any way to distribute it as a command-line package and using in the swift file?
import Foundation
struct Task {
static let shared = Task()
func run(with args: String...){
let task = Process()
task.launchPath = "/usr/bin/git"
task.arguments = ["-c", args.joined(separator: " ")]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
print(output)
task.waitUntilExit()
}
}
let task = Task.shared
task.run(with: "status")
task.run(with: "fetch --all")
task.run(with: "add --all")
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

How can I create Daemon in LaunchAgents from macOS APP?

I want to create Daemon (Plist in ~/Library/LaunchAgents/) from my macOS application.
import Foundation
func shell(_ command: String) -> String {
let task = Process()
let pipe = Pipe()
task.standardOutput = pipe
task.arguments = ["-c", command]
task.launchPath = "/bin/bash"
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)!
return output
}
// Example usage:
shell("ls -la")
I know using this I can create a daemon but at this location
~/Library/LaunchAgents/
I am not able to create due to permission issues. Operation not permitted
But using terminal I am able to Create Plist inside
~/Library/LaunchAgents/

How to show real time result while using terminal command in a swift script

I am using Xcode Project Renamer to rename my Xcode project, after that I used the code below to install pod file.
It's working good but the terminal showing result after pod installed. I want to show result while installing the pod.
#discardableResult
private func shell(_ command: String) -> String {
let task = Process()
task.launchPath = "/bin/bash"
task.arguments = ["-c", command]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
// pipe.fileHandleForReading.readDataToEndOfFile()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue)! as String
return output
}
let command = shell("pod install")
print(command)
Can someone help please.