Zcore: common Unix utilities in Zig

Repo: zcore

This is more a learning exercise than a serious project, but it’s helping me find my way around the standard library. Turns out that syntax was the easy part of course. I would welcome any PR’s and/or comments on the code to further my knowledge.

Known deficiencies:

  • head is Posix compliant but is often expected to take the old style head -1 type arguments, but this one doesn’t right now.
  • base64 is unfinished. When done, it will optionally print a header to identify the file, much like head does.
  • My first couple additions here were so simple I didn’t bother running zig init-exe, so they have to be build with zig build-exe and not zig build.
  • There’s currently no overall build system, so each utility is standalone and built from it’s own subdirectory.

I’m having fun though. Zig makes programming fun :grinning:


Added readlink this morning. I’ll be doing uname next.

I went through a similar exercise of implementing tools following the Posix specs and, yes, it’s a great way to learn Zig!

My “coretools” repository currently has basename, cat, dirname, echo, false, nohup, true, tsort. I should continue that one day. :slight_smile:


Hope I didn’t step on your toes doing this then. If you do feel like giving it another go, I’d be glad to collaborate. Might turn into something real if someone had a kernel in Zig and wanted Zig utilities?

1 Like

Well done!

This is indeed a good way to learn a ton about a language and its standard library!

Also an opportunity to build tools with unique features that make them cooler than the originals :slight_smile:

The code looks great!

Buffers with a fixed size or upper bound, such as in the base64 utility, could be reused by processing the input as a stream. That way, you can make them smaller, while allowing arbitrary large inputs.

But this is nice and glad to hear that you are enjoying Zig!

Thanks for the code suggestions. I didn’t feel especially great about that particular part of the code, so I’ll gladly take another look bearing that in mind.

1 Like

Huh? No, of course not! I don’t have an exclusive on the implementation of coretools :sweat_smile:

In fact it just occurred to me that I saw another similar project some time ago through the awesome-zig repo: GitHub - pbui-project/pbui-main: The main repository for the PBUI project

Added the uname utility. I’m probably going to take a break from adding programs next and see if I can do some work on the build system, so that the entire project can be built at once.

I love it. I have a couple utilities in mind that I would like to try porting to

  1. Learn how they work
  2. Sharpen my Zig
  3. Add features I want
  4. ???
  5. Profit!

It’s great to see other folk’s implementations of these core utils in particular. Since they’re so small and their behavior is so well known, it’s easy to map the mental model of the mechanism to the code.

1 Like

This is, for me, the third language that I’ve written a couple of these utilities in after C and Rust, respectively. To me, implementing the same thing in a different language really helps to get a better picture of the way the language informs the style.

It also gives you a nice mental view of how Unix grew in it’s early days when you start looking at the standard library and realize that a lot of those two letter programs were just exposing the C library functions to the command prompt. And comparing what I see in Zig with what I know is in C, if you ignore the namespaces which C doesn’t have, there’s often a one to one correlation between a Zig and aC counterpart. Which makes it feel very comfortable. I can see that there’s probably a lot of code that I could port as an almost literal translation.

1 Like

So with a little help understanding the zig build system a little better I’ve been able to greatly simplify building the code. The sources have been moved from individual project directories into bin/src/<utility-name>.zig and usr.bin/src/<utility-name>.zig and are built in a loop in build.zig. Much simpler than I thought it was going to be.

More importantly to me, I got a working hostname utility, even without an interface to sethostname in Zig’s std. This marks the first time for me calling a syscall directly from Zig, and it feels good to do something that low level.


After taking a little time away from Zig to finish a Rust project (at least to get the 1.0 release done), I came back to this yesterday. This time I started with blkid, which is inherently not going to be portable beyond Linux. So I made the decision to a. put it into a separate repo and b. relax my restriction that the code can’t call into or link to C libraries. This was pretty necessary to avoid reinventing too many wheels.

Anyway, the second repo is here. I’m nowhere near done with this one, but I’m going to leave it at this stage for today. Some things to note:

  • I’m not going to try to exactly copy the command line options of the util-linux version of this utility, both because I think that many of the options are unneccessary and I want to maintain some consistency in interface between the other tools I write. I’m hugely not a fan of the util-linux project, and find it painful to even use one of their libraries, but reducing the need for the package is part of why this was one I wanted to tackle.
  • Right now the interface options that I intend to offer are mostly unimplemented. At this point you can only pass one or more block devices as parameters, which are not themselves partitions, and output the result with default formatting.
  • I’ve yet to figure out how to get the PARTUUID field.

I have a little toy distro that I’ve been working on for a while, and I’m using a mix of BSD userland that I’ve ported to Linux, suckless tools from sbase and ubase, and a number of my own userland tools. A deficiency of using either BSD or suckless mount on Linux is that you lose the ability to mount a partition using the UUID or to specify the partition that way in fstab. So that’s the longer reason that I started on blkid, so that I can learn this API a little bit and eventually translate that into a more functional mount command.