Zig NEWS

LI Yu
LI Yu

Posted on

`zcmd.zig`: a `std.childProcess.run` replacement but with the ability of running `bash` pipeline

zcmd.zig is a small lib I have been working on (while try to compile C/CPP legacy libs with zig)

GitHub logo liyu1981 / zcmd.zig

zcmd is a single file lib to replace zig's std.childProcess.run with the ability of running pipeline like bash..

zcmd.zig

zcmd is a single file lib (zcmd.zig) to replace zig's std.childProcess.run. It has almost identical API like std.childProcess.run, but with the ability of running pipeline like bash.

Example like execution of single command (replacement of zig's std.childProcess.run)

const result = try Zcmd.run(.{
    .allocator = allocator,
    .commands = &[_][]const []const u8{
        &.{ "uname", "-a" },
    },
});
Enter fullscreen mode Exit fullscreen mode

the differences to std.childProcess.run is it will take commands instead of single command.

It can run a bash like pipeline like follows (to recursively find and list the latest modified files in a directory with subdirectories and times)

const result = try Zcmd.run(.{
    .allocator = allocator
    .commands = &[_][]const []const u8{
        &.{ "find", ".", "-type", "f", "-exec"
Enter fullscreen mode Exit fullscreen mode

what it is?

Simply to say, it is a std.childProcess.run replacement, but with the ability of running bash pipeline.

Below is a good example from one of my other project how this can be used

    const pod_version = brk: {
        cwd.access(".git", .{}) catch {
            const result = try zcmd.run(.{ // mark 1
                .allocator = allocator,
                .commands = &[_][]const []const u8{
                    &.{ "head", "-n", "1", "Changelog" },
                    &.{ "cut", "-d", "(", "-f", "2" },
                    &.{ "cut", "-d", ")", "-f", "1" },
                },
            });
            std.debug.print("\n{any}\n", .{result});
            result.assertSucceededPanic(.{});
            break :brk result.trimedStdout();
        };

        // do not try to download git as originam CMakeList does. Do you guys have git?
        const result = try zcmd.run(.{ // mark 2
            .allocator = allocator,
            .commands = &[_][]const []const u8{&.{ "git", "--git-dir=./.git", "describe", "--abbrev=4", "HEAD" }},
        });
        result.assertSucceededPanic(.{});
        break :brk result.trimedStdout();
    };
Enter fullscreen mode Exit fullscreen mode

The above code is running pipelines like in bash to generate a version string. In mark 1 case, it will run head -n 1 Changelog | cut -d ( -f 2 | cut -d ) -f 1, and in mark 2 case it will run git --git-dir=./git describe -- abbrev=4 HEAD.

The motivation for this lib is that I found std.childProcess.run can only run one command, so assemble a pipeline will be tedious (and not efficient actually), and on the otherside, std.Build.addSystemCommand supports only one command too.

usage

Use it like typical zig pkg

zig fetch --save https://github.com/liyu1981/zcmd.zig/archive/refs/tags/v0.2.1.tar.gz
Enter fullscreen mode Exit fullscreen mode

More frequently you will want to use it in build.zig, which zcmd.zig supports well. After added it to build.zig.zon,

// when import in build.zig, the zcmd is exposed in nested const zcmd
const zcmd = @import("zcmd").zcmd;
// then next can use zcmd.run as above
Enter fullscreen mode Exit fullscreen mode

or browse this example.

The API provided by zcmd.zig is almost identical to std.childProcess.run (besides it will accept commands instead of command). There are also other convenient help functions like those you may already see to assert results.

how it is implemented

zcmd.zig is implemented with the same algorithm used by bash. Essentially, the pipeline is started from first command to last command; each command will be run in the current process; and a child process is forked for executing possible next one; all commands will have unix pipes connected them together.

And because of the algorithm, it is currently only supporting linux/macos, windows is left out because I do not really know whether there are pipes and how they work there. If you want to help me to get this done, feel free to send a pull request, or point me to how to do it. Thanks.

examples and docs

Examples can be found in the repo.

Docs can be found here.

Top comments (1)

Collapse
 
kristoff profile image
Loris Cro

Cool project, thanks for sharing!