Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

You can already do this without using binfmt ...

  #if 0
  gcc "$0" -o "$@".out && exec ./"$@".out
  #endif
  #include <stdio.h>
  int main () { printf ("hello, world\n"); return 0; }
Usage:

  $ chmod +x print.c
  $ ./print.c
  hello, world
(Could be better to use a temporary file though.)

There's a similar cute trick for compiled OCaml scripts that we use with nbdkit: https://libguestfs.org/nbdkit-cc-plugin.3.html#Using-this-pl...



The use of $@ doesn't look right to me.

In the trivial case exposed here where there are no additional arguments to pass to the .c program, the shell executes

  gcc "print.c" -o .out && exec ./.out
and it works "by chance".

In a more complex scenario where print.c expects some parameters, it won't work. For example,

  ./print.c a b c
will result in the shell trying to invoke

  gcc "print.c" -o "a" "b" "c".out && exec ./"a" "b" "c".out
which makes no sense.

Are you sure you didn't intend $0 instead of $@ ?


It's true, that's a mistake!

OTOH we're trying to write self-compiling executable C scripts, so the safety, correctness and good sense ships sailed a while back.


Compiler errors won’t cause as many funny consequences with

  gcc "$0" -o "$@".out && exec ./"$@".out || exit $?   # I'd use ${0%.c} not $@
Love this trick too, but the difference, as far as I understand, is that it only works with a Bourne(-compatible) shell, whereas shebangs or binfmt_misc also work with exec().


Oh this is neat. Took me a bit.

The shell treats the first line as a comment. It executes the second line, which eventually exec's the binary so the rest of the file do not matter to the shell.

And the compiler treats the first line as a preprocessor directive, so it ignores the second line.

I initially misread/mistook the first line for a shebang.


You can also #embed the compiler binary, and execve it to much the same effect as binfmtc. I explored that trick for an IOCC entry that was never submitted because it ended up far too readable.


Is there a way to do this and have the shell remove the temporary file after exec?


Yes, but it's not worth it. It's better to forget gcc and use tcc instead, which has the -run flag to compile and run without creating any intermediate file. It's also much quicker than gcc.


There are many, no doubt

   #if 0
   cc -static -pipe -xc "$0"||exit 100
   exec ./a.out
   #endif
   int puts(const char *);
   int unlink(const char *);
   int main(int argc,char *argv[]){ 
   puts("it works"); 
   unlink(argv[0]);
   return 0; 
   }


Or even

    /*bin/true ; exec tcc -run "$0" "$@" # */


I'd have expected this to need a hashbang (#!/bin/sh) at the beginning. Why doesn't it?


Because your shell will execute anything that looks like a text file & has +x bit set as a shell script.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: