Use of posix_spawn? - swift

I try to start a command file from my app, so I need to use "posix_spawn". But usage of this requires strange use of pointers. I didn't find any example of Swift (3.0), only C++ or Objective C which I couldn't translate.
I simply need something like that (in old system call):
let err = system("ls -param >file.txt")
Any ideas?
Edit:
The linked solution didn't match.
First it does not use posix_spawn function, which was mentioned by the complier. It uses NSTask, which seems also be abandoned. But I tried this example and ended up in:
func shell(launchPath: String, arguments: [String]) -> String
{
let task = Process()
task.launchPath = launchPath
task.arguments = arguments
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue)! as String
return output
}
Here all changes needed for XCode 8 were made. But when calling, it will never return from ".launch()".
Somewhere in Output debug window I found this line:
2016-09-15 15:06:36.793 DSRenamer[96562:2569582] launch path not accessible
Same command works fine in terminal window:
/usr/local/bin/ExifTool -ext .CR2

I use swift to call the objective-c menthod(though not the best way)
define an objective-c util menthod
#import "SystemTaskHelper.h"
#implementation SystemTaskHelper
+(void)performSystemTask:(NSString *)task
{
system([task UTF8String]);
}
#end
import objective-c util.h into Bridging-Header.h , and in swift use
SystemTaskHelper.performSystemTask("ls -param >file.txt")

Related

Bash command not found using 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.

Run shell command with pattern in Swift

I would like to run a shell command on many files that should match on a given filename regex. I found this code snippet that runs a shell command with arguments:
func shell(_ arguments: [String] = []) -> String {
let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = arguments
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8) ?? "unknown"
return output
}
It runs great, but it does not resolve the parameters:
shell(["ls", "~/Desktop/*.txt"])
Does not resolve the * to all txt files, it tries to only work on a file called *.txt. Is there some option I need to set on Process?
Thanks in advance for your help!
I just found out the answer! The resolving of * and other patterns is done by the shell, Process only runs a given command. So the solution is to create a shell and run the command in there: (will do some clean up in the code, but this works)
shell(["bash", "-c", "ls ~/Desktop/*.txt"])

How to do a "say" in Swift 3 inside command-line program?

What should I do in this case with the following code?
func convertToM4A(filename: String, voice: String) -> Bool {
let full_string = speaking_queue?.joined(separator: " ")
let command_string: [String] = [/"-v \"\(voice)\"",*/ "--progress", "--output-file=\"\(filename)\"","-i", " \"\(full_string!)\""]
print(command_string)
/
let DocumentsDirectory = FileManager().homeDirectory(forUser: "shyamalchandra")
print((DocumentsDirectory?.absoluteString)!)
*/
let task = Process()
task.launchPath = "/usr/bin/say"
task.arguments = command_string
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output: String? = String(data: data, encoding: String.Encoding.utf8)
task.waitUntilExit()
if let output = output {
if !output.isEmpty {
print(output.trimmingCharacters(in: .whitespacesAndNewlines))
}
}
return true
}
At run-time, it complains about the TERM environment being not set and furthermore, doesn't write the file to disk. What to do?
The main error is how you build the argument array. The given arguments
are passed directly to the process. Process does not use the shell to
interpret the arguments, therefore you must not enclose them in quotation
marks.
Another problem is that the "-i" (interactive) option cannot be used
when writing to a file.
So your code should look like this:
func convertToM4A(filename: String, voice: String) -> Bool {
let fullString = "Hello world"
let task = Process()
task.launchPath = "/usr/bin/say"
task.arguments = [ "-v", voice, "-o", filename, fullString]
task.launch()
task.waitUntilExit()
return true
}
The "--progress" option causes a progress meter to be displayed on
standard error. If you want to display that then you would have to
read asynchronously from standard error.
If you're writing a native Mac app and want to record synthesized speech to an audio file, don't go trying to wrap a shell command — there's native API
for that. NSSpeechSynthesizer is the macOS API for text-to-speech in general, and it has a method startSpeaking(_:to:) that records output to an audio file.
This API outputs to an AIFF file, but there are numerous APIs you can use to convert/encode that to M4A: AVAssetReader/AVAssetWriter, AVAudioFile, lower-level CoreAudio C APIs, etc.
(Generally, if you're writing a native Mac program and there's something you want to do, check to see if there's an API for it before you go trying to wrap a shell command. Usually those shell commands are using the same API, so you're just punishing yourself with all the indirection, I/O parsing, etc.)
Yes, NSSpeechSynthesizer is an AppKit API, but you can use it in a command line tool.
Take a look at this lib, I've used it before and it is very capable of running shell script. With that then you can use the "say" command and send in some arguments. https://github.com/kareman/SwiftShell
You could try it this way for instance
import SwiftShell
try runAndPrint("say", "Hello world", 4, "arguments")
let array = ["Hello world", "we", "are"]
try runAndPrint("say", array, array.count + 2, "arguments")

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

Handling special characters in Swift

I'm developing a Cocoa App in Swift, but I'm stuck trying to do something that must be easy to do but somehow I'm not getting the result I expect.
In my application, I'm trying to hash a word to MD5. Everything works fine when I skip special characters. When I try to hash a word with special characters, the resultant hash doesn't correspond to the correct hash.
Here's my code.
import Cocoa
let task = NSTask()
let pipe = NSPipe()
task.standardOutput = pipe
task.launchPath = "/bin/bash"
task.arguments = ["-c", "echo -n 'Canción' | md5"]
let file:NSFileHandle = pipe.fileHandleForReading
task.launch()
task.waitUntilExit()
let data = file.readDataToEndOfFile()
let datastring = NSString(data: data, encoding: NSUTF8StringEncoding) as String!
print("Hash = \(datastring)")
If you run this code in a Playground, you'll get this hash:
Hash = 770f92a03e9c82af426549941a9df70b
But, if you run the next command directly in your OS X terminal, you'll get the correct hash, which is Totally different to the first one.
$ echo -n 'Canción' | md5
This is the terminal response:
3abf536a5262c902a30d74f68f38067b
It is clear that I'm missing something important, but I can't find what's in my code.
Anyone?
Please... help! 😅