5

I'm trying to chain shell command together in a Swift script. The actual commands in question are the output of gource piped to the input of ffmpeg, but here's a simplified, contrived example of what I'm trying to do:

let echo = Process()

echo.launchPath = "/usr/bin/env"
echo.arguments = ["echo", "foo\nbar\nbaz\nbaz", "|", "uniq"]

let pipe = Pipe()
echo.standardOutput = pipe

echo.launch()
echo.waitUntilExit()

// Get the data
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue)

print(output ?? "no output")

Expected output:

foo
bar
baz

Actual output:

foo
bar
baz
baz | uniq

The | is being interpreted as part of the last argument. How can I chain commands together, so that data flows from one to the next, in a Swift script? I tried various combinations of assigning standardIn and standardOut and using Pipe() between two Processes, but either I'm doing it wrong, or I'm not connecting the right pieces together.

3 Answers 3

12

I got an answer with help from zadr:

import Foundation

let pipe = Pipe()

let echo = Process()
echo.launchPath = "/usr/bin/env"
echo.arguments = ["echo", "foo\nbar\nbaz\nbaz"]
echo.standardOutput = pipe

let uniq = Process()
uniq.launchPath = "/usr/bin/env"
uniq.arguments = ["uniq"]
uniq.standardInput = pipe

let out = Pipe()
uniq.standardOutput = out

echo.launch()
uniq.launch()
uniq.waitUntilExit()

let data = out.fileHandleForReading.readDataToEndOfFile()
let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue)

print(output ?? "no output")
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! I spent way too long trying to work this out and trying to find ways around it.
0

For others that may be running into the same problem, here's what I derived from Zev's answer to fit with creating a SHA1 HMAC hash:

func getHMAC(forValue value: String, key: String) -> String {

    let pipe = Pipe()

    let echo = Process()
    echo.launchPath = "/usr/bin/env"
    echo.arguments = ["echo", "-n", value]
    echo.standardOutput = pipe

    let task = Process()
    task.launchPath = "/usr/bin/env"
    task.arguments = ["openssl", "sha1", "-hmac", key]
    task.standardInput = pipe

    let output = Pipe()
    task.standardOutput = output

    echo.launch()
    task.launch()
    task.waitUntilExit()

    let data = output.fileHandleForReading.readDataToEndOfFile()
    return NSString(data: data, encoding: String.Encoding.utf8.rawValue) as! String
}

This is especially useful when making simple API calls from Swift scripts which require a hash in the HTTP header. As far as I found, this was the only way it was possible (since we cannot make use of the Objective-C CommonCrypto framework in Swift scripts)

Comments

-1

You should take a look a this. It's an example of an easy way to pipe by chaining. https://gist.github.com/eahrold/b5c5fd455225a8726e1cc31708e139db

so you can something like

 let ls = Process("/bin/ls", ["-al"])
 let grep = Process("/usr/bin/grep", ["com"])
 let cut = Process("/usr/bin/awk", ["{print $3}"])

 ls.pipe(grep).pipe(cut).complete {
    message, status in
    print("Your results: \(message)")
 }
 ls.launch()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.