Swiftpack.co - Zollerboy1/SwiftCommand as Swift Package

Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
See all packages published by Zollerboy1.
Zollerboy1/SwiftCommand 1.4.0
A wrapper around Foundation.Process, inspired by Rust's std::process::Command.
⭐️ 75
🕓 38 weeks ago
macOS
.package(url: "https://github.com/Zollerboy1/SwiftCommand.git", from: "1.4.0")

SwiftCommand

Platform: macOS/Linux/Windows* Swift versions: 5.6, 5.7, 5.8

*Windows support is only experimental for now.


A wrapper around Foundation.Process, inspired by Rust's std::process::Command. This package makes it easy to call command line programs and handle their I/O.

Installation

You can install this package using the Swift Package Manager, by including it in the dependencies of your package:

let package = Package(
    // ...
    dependencies: [
        // other dependencies...
        .package(
            url: "https://github.com/Zollerboy1/SwiftCommand.git",
            from: "1.2.0"
        ),
    ],
    // ...
)

Usage

Using this package is very easy.

Before you start, make sure that you've imported the SwiftCommand module:

import SwiftCommand

Now it can be used like this:

let output = try Command.findInPath(withName: "echo")!
                        .addArgument("Foo")
                        .waitForOutput()

print(output.stdout)
// Prints 'Foo\n'

This blocks the thread until the command terminates. You can use the async/await API instead, if you want to do other work while waiting for the command to terminate:

let output = try await Command.findInPath(withName: "echo")!
                              .addArgument("Foo")
                              .output

print(output.stdout)
// Prints 'Foo\n'

Specifying command I/O

Suppose that you have a file called SomeFile.txt that looks like this:

Foo
Bar
Baz

You can then set stdin and stdout of commands like this:

let catProcess = try Command.findInPath(withName: "cat")!
                            .setStdin(.read(fromFile: "SomeFile.txt"))
                            .setStdout(.pipe)
                            .spawn()

let grepProcess = try Command.findInPath(withName: "grep")!
                             .addArgument("Ba")
                             .setStdin(.pipe(from: catProcess.stdout))
                             .setStdout(.pipe)
                             .spawn()

for try await line in grepProcess.stdout.lines {
    print(line)
}
// Prints 'Bar' and 'Baz'

try catProcess.wait()
try grepProcess.wait()
// Ensure the processes are terminated before exiting the parent process

This is doing in Swift, what you would normally write in a terminal like this:

cat < SomeFile.txt | grep Ba

If you don't specify stdin, stdout, or stderr, and also don't capture the output (using e.g. waitForOutput()), then they will by default inherit the corresponding handle of the parent process. E.g. the stdout of the following program is Bar\n:

import SwiftCommand

try Command.findInPath(withName: "echo")!
           .addArgument("Bar")
           .wait()

GitHub

link
Stars: 75
Last commit: 38 weeks ago
Advertisement: IndiePitcher.com - Cold Email Software for Startups

Release Notes

38 weeks ago

This release removes the check from Command.init(executablePath:) that looks if there is an executable file at the given path. Apparently Foundation.FileManager.isExecutableFile(atPath:) can have false negatives, in which case it would be impossible to initialize Command with a custom executable file, as the initializer would always throw an error.

Thanks to @hisaac for pointing this out to me and for implementing the fix.

Furthermore, ChildProcesses that have both a piped stout and stderr now have a property mergedOutputLines that allows you to get the stderr and stdout output of a process in one async sequence.

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics