Shebang for C .... Almost

Shebang for C .... Almost

Bradley Noyes published on
3 min, 527 words

Shebang for C ... Almost🔗

Sometimes I need to write a small C program that lives as a single C file. It's too much overhead to write a separate Makefile to compile one C file, so how do you easily remember how to compile it? If there are no dependencies (i.e. library or include paths) then its pretty straightforward to run gcc or clang by hand. But what if there are a handful of dependent libraries that need to be linked to the program or include lines? Crafting that compile line can a frustrating exercise in searching your shell history. A possible solution is to embed the compile line embedded at the top of the C file, like so:

    /*
     * to compile: gcc hello.c -o hello -ldl -m -L/usr/local/lib -L/other/long path/my_lib/ -lmy_lib ...
     */
    ...
         
    int main() { ... }

But that's amature hour, here's how you really should do it ...

I've been using a similar -- but more functional and user-fiendly -- approach. I embed a tiny shell script in the C file. Its almost (but not quite) a shebang method for C. I feel that my method is kind of an abuse of the C preprocessor, but hey, i'm pretty sure the C preprocessor has seen much worse.

Take a look at the example below:

    #if 0
    
    # embed shell script here ...
    
    # setup variables, e.g. this_file=hello.c this_exec=hello
    # this assumes the c-file is in your current directory, i use
    # `basename` to strip out any ./ prefix that i put in front of
    # scripts by habit.
    this_file=`basename $0`
    this_exec=`echo $this_file | cut -d . -f 1`
        
    # with this compile line below you can put in your needed
    # link/include paths so you don't need to remember them, 
    # my example isn't that elaborate.
    exec_line="gcc $this_file -o $this_exec -ggdb -ldl"
    echo "exec: $exec_line"
    
    eval $exec_line
    
    if [ $? -eq 0 ]; then
        echo "Compile succeeded: $this_file ---> $this_exec"
        echo "Running $this_exec ... "
        echo ""
        eval ./$this_exec
        exit $?
    else
        echo "Compile error"
        exit 1
    fi
    
    #endif // 0
        
    #include <stdio.h>
    int main()
    {
        printf("Hello, Stupid C-trick\n");
        return 0;
    }

Because the preprocessor strips out everything between #if 0 and #endif, the C file will compile just fine on its own, without modification. And, because in Bash style shells (e.g. bash, sh, zsh, et al) the # symbol is a comment, those preprocessor lines are ignored by the shell and the embedded script will execute until it hits an exit.

To compile and run the program just run the C-file with bash or zsh or sh (depending on the default shell for your OS),

    Darwin%› sh hello.c
    exec: gcc hello.c -o hello -ggdb -ldl
    Compile succeeded: hello.c ---> hello
    Running hello ...
    Hello, Stupid C-trick

I find it to be a useful method for single C-file projects. I'm sure other similar methods must exist. I can image a script which outputs C-code and compiles it, however that's more like embedding C-code in a shell script, whereas I prefer embedding a shell script in C-code so the C-code can stand own its own without modification. You can go pretty crazy with this kind thing, so expand at your own risk!

Enjoy!