8 ms·
C89cc.sh – standalone C89/ELF64 compiler in pure portable shell
- gaigalas 7mo agoSingle standalone file, no external tools used, PATH='' (empty), portable (bash, dash, ksh, zsh), produces x86 ELF executables, has mini-libc builtin. Usage: printf 'int main(){puts("hello");return 0;}' | sh c89cc.sh > hello chmod +x hello ./hello
- angry_octet 7mo agoI can't think of a reason to use c89cc.sh, but I salute this effort nonetheless.
- gaigalas 7mo agoThe main show are the techniques for writing portable shell scripts, not the compiler. If you want something one would actually use, try my project tuish: http://github.com/alganet/tuish http://github.com/alganet/tuish
- honkcity 7mo agoIs there a linter to ensure scripts are portable across shells? I try to write them like that but I'm certainly no master so I write them to work with busybox.
- gaigalas 7mo agoA linter, not yet. You can use what I use: https://github.com/alganet/shell-versions https://github.com/alganet/shell-versions It's a container with lots of shells that you can test. Like esvu but for the shell. Might have a little outdated docs, hit me with an issue if you use it and face any problems (I'm also the author).
- pvtmert 7mo agoI think shellcheck helps quite a lot, you must set type to "sh" (not bash) somewhere in the comments though...
- jonahx 7mo agogorgeous!
- t-3 7mo agoWhy not POSIX or some common external tools where it makes sense? Most of those big switch statements could be easily replaced with some standard programs that already exist everywhere.
- gaigalas 7mo agoOne main reason is performance. Forking for other tools is very expensive. That said, using larger sed or awk programs instead of ad-hoc calls for small snippets would perhaps be net-positive for performance and readability. I'm currently working on very strict bootstrap scenarios in which sed and awk might not be available, but a shell might be (if I'm able to write it). It is possible that in such scenarios, the fist send and awk versions will be shell-written polyfills anyway.
- MisterTea 7mo ago> One main reason is performance This assumes the executed program is as fast or slower than the caller.
- Brian_K_White 7mo agoWhy not just use gcc which already exists everywhere? When you answer that, same answer. If you can't imagine any answer for that, then the answer won't be convincing or make sense even if anyone tried to articulate it. Which is fine. Everyone doesn't have to find meaning in the same things.
- t-3 7mo agoShell without a userland is like FORTH without the ability to define new words. It's really contrary to the whole idea of what a shell is. Bootstrapping in very constrained conditions makes some sense, but where would you have a POSIX shell and not a POSIX userland (or close equivalent) to work with? When I wrote a similar compiler in shell, I purposely offloaded everything I could to external tools and used the shell for composition, so I found the approach intriguing and wanted to ask. I wasn't trying to criticize or dismiss the project, I think it's really cool or else I wouldn't have bothered to read the code in the first place.
- kelsey98765431 7mo agoWould be a lot better if it came with tests. Please do this justice and dont let it rot as a gist, make a real repo and add some docs and at least smoke tests or some kind. Thanks
- fuhsnn 7mo agoDon't understand why you were downvoted. An untested C compiler is simply worthless.
- nananana9 7mo agoThe 2026 brain simply cannot comprehend recreational programming.
- fuhsnn 7mo agoWell, I happen to have been recreationally maintaining a hobbyist C compiler for three years, adding tests is part of the fun.
- gaigalas 7mo agoYou want to know what kinds of programs it can run, right? Look at this one: https://gist.github.com/alganet/1513d7b6abef5c1a53a324d897c358e0 https://gist.github.com/alganet/1513d7b6abef5c1a53a324d897c3... shell.c is a shell interpreter written for c89cc.sh. It can do the full self-hosting ouroboros: - c89cc.sh can compile shell.c - compiled shell.c via c89cc.sh can run c89cc.sh It's not a full blown battle tested shell interpreter yet, but I'm working on it. This file is part of the bootstrapping setup I'm working on for very early (pre tinyc) bootstrap from source in x64 machines and it is by far the most complicated program c89cc.sh can compile.
- fuhsnn 7mo agoThanks, that actually look like a very solid baseline to start things with. Are you aware of onramp[1]? They use a custom VM to base compiler and shell on, it's extra steps, but could be more flexible long term. [1] https://github.com/ludocode/onramp https://github.com/ludocode/onramp
- dmitrygr 7mo agoMany parts of this are clearly autogenerated, but that in no way diminishes the sickening impressiveness of it!
- phire 7mo agoIt does make it a little hard to understand how the parser/ast_builder works. But the rest seems easy enough to understand.
- fuhsnn 7mo ago> It does make it a little hard to understand Or much easier to backdoor...
- gaigalas 7mo agoThis is a hand-written parser for the shell itself (portable shell parsing portable shell): https://gist.github.com/alganet/23df53c567b8a0bf959ecbc7b68997c0 https://gist.github.com/alganet/23df53c567b8a0bf959ecbc7b689... It's an incomplete idea from around a year ago. The approach taken here (aliases as macro-like evals, AST generation using shell variables) became the backbone for the BNF parser generator. This one is much simpler to understand. Simpler grammars tend to produce parser code that looks more like this one.
- userbinator 7mo agoIt would be far more interesting to look at what this was "compiled" from; it looks like the output of a state-machine generator.
- gaigalas 7mo agoThe source for BNF generator is here: https://gist.github.com/alganet/4dfd501a3377a60f7825901114d65c77 https://gist.github.com/alganet/4dfd501a3377a60f7825901114d6... Roughly 70% of c89cc was generated from it (parser, emitter). It can generate parsers for C, ES6 and XML for example (subsets but not missing a lot). It's still a mess though and I have lots of work to do to a proper release.
- jey 7mo agoIt targets x86-64/ELF? I thought it would target `sh` to be portable?
- _ache_ 7mo agoI'm tempted to execute it, but it may as well be shellcode I couldn't tell.
- JackSmith_YC 7mo ago[dead]
- tho2u3i4o23497 7mo agoNode stuff atleast "works" - you've not seen real dependency hell until you've seen the horrible world of the Python ML ecosystem.
- self_awareness 7mo ago"Claude please generate me a C compiler in bash" I mean, today it's possible to generate it in Tcl, Elisp, Windows BAT, Powershell. The effort is just 1 prompt. The WHY question is much more important today -- "because I can" no longer makes sense, because we all can do much, much more with minimum effort today than before LLMs.
- gaigalas 7mo agoHere's a prototype parser from 10 months ago, when this was not possible yet: https://gist.github.com/alganet/23df53c567b8a0bf959ecbc7b68997c0 https://gist.github.com/alganet/23df53c567b8a0bf959ecbc7b689... Here is me 10 years ago experimenting on parsing stuff with sed: https://gist.github.com/alganet/542f46865420529c9bd2 https://gist.github.com/alganet/542f46865420529c9bd2 --- Yes, c89cc.sh was definitely AI-assisted. However, I do carry extensive knowledge of the portable shell that was essential for the AI to complete it. You'll find tricks inside c89cc.sh that don't exist anywhere, except in other code from me (like the ksh93 fix for local dynamic scoping or the alias/macro read -n1 polyfill). The WHY is pretty obvious: I want to show that the portable shell is not a toy.
- self_awareness 7mo agoI didn't mean to imply that you're not capable doing it without LLM. I believe you. The point I'm trying to make is that the rest of us that don't know bash that well is capable of doing it as well. This is the new reality we all need to adapt to.
- fkoep 7mo ago
- uecker 7mo agoI am tempted to click the "report abuse" link ;-)
- dmead 7mo agoThis is vibe coded right?
- gaigalas 7mo agoIt is AI assisted, but way less than one might think. All the portability tricks are authored by me (I have previous repos from before the AI era that feature them). The parsing structure is also authored by me (also have pre-AI proof). What AI did here was the boring, mechanical work. Just prompt it, all AI as of 2026 is clueless about wide portability shell scripts.
- wengo314 7mo agoif one could boostrap tcc with it, then it might be a viable tool.
- redoh 7mo ago[flagged]
- lpribis 7mo agoI don't think any realistic system will bootstrap C from shell. What is the shell implemented in if not C?
- gaigalas 7mo agoThis shell.c can be compiled from c89cc.sh itself: https://gist.github.com/alganet/1513d7b6abef5c1a53a324d897c358e0 https://gist.github.com/alganet/1513d7b6abef5c1a53a324d897c3... Ouroboros self-hosting. They can self-host one another. The idea is to make shell.c compile from an even simpler C compiler, such as M2-Planet: https://github.com/oriansj/M2-Planet https://github.com/oriansj/M2-Planet Let me remind you that current stage0 bootstraps tinyc from mes, which is an interpreted lisp. It's not that different from the shell architecturally. The current stage0 also features kaem as one of the first dependencies. kaem is, in fact, a simpler version of the bourne shell. It's always a tower. You'll never get a one single clean dependency pass in bootstrapping.
- zhongwei2049 7mo agoThe shell.c ouroboros is really cool. Being able to bootstrap trust through an entirely different language family (shell → C → shell) adds genuine value to the trusting-trust problem beyond just technical novelty.
- cestith 7mo agoEven if your shell was compiled in C, it doesn’t mean it wasn’t cross-compiled from another platform.
- xonix 7mo agoBut how would POSIX shell be available? Wouldn’t it be compiled from C?
- cestith 7mo agoI love this as a novelty, and it could be useful for bootstrapping a system that’s had a shell cross-compiled to it. Thinking about this in the context of a job I used to do, security on shared hosting environments, it gives me a bit of a shiver. There are reasons compilers aren’t available to normal users on those.