execute c source files
tl;dr:
- add
//usr/bin/gcc "$0" && exec ./a.out "$@"
to the first line of yourexample.c
- make executable:
chmod +x example.c
- run it:
./example.c
I’ve a folder called lab
for those quick ‘n’ dirty programs to test some language features or other interesting behavior. While golang
programs can be easily executed via go run <go file>
and this is not the case for c programs needing a built step. There is AFAIK no such command like gcc run
.
Shebang
Linux’s execve(2)
syscall supports shebangs. When the first line starts with #!
aka shebang, it will get parsed by the kernels binfmt_script module and used for executing the rest of the file. This enables to run various non-binary scripts (bash, python, ruby, ./program
without using the interpreter.
Can we use a shebang for c source files? tl;dr: nope, as far as I tried this is not possible.
I’ve ended up with something like:
#!/usr/bin/tail -n+2 main.c /tmp/main.c && /usr/bin/gcc /tmp/main.c -o main && ./main #
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
for(int i = 0;i<argc; i++) {
printf("%d: %s\n", i, argv[i]);
}
}
/*
* $ ./main.c 1 2 3
* /usr/bin/tail: invalid number of lines: ‘+2 main.c /tmp/main.c && /usr/bin/gcc /tmp/main.c -o main && ./main #’
*/
This doesn’t work while everything after tail
gets interpreted as one argument.
binfmt fallback
Just before I was about to give up, I’ve found an SO post suggesting:
//usr/bin/clang "$0" && exec ./a.out "$@"
I’ve adapted it to my use-case and voila:
//usr/bin/gcc "$0" && ./a.out "$@";exit
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
for(int i = 0;i<argc; i++) {
printf("%d: %s\n", i, argv[i]);
}
}
/*
* $ ./main.c 1 2 3
* 0: ./a.out
* 1: 1
* 2: 2
* 3: 3
*/
//
: c single-line comment to avoid gcc to interpret this line//usr/bin/gcc "$0"
: compile file itself using the absolute path to the compiler, multiple slashes will automatically compacted to one, so // is no problemexec ./.aout "$@"
: execute the built binary (nameda.out
per default) with all arguments which were initially added to the program call./program arg1 arg2...
, exec replaces the current process which is important as the rest of the c source file otherwise gets interpreted
Well, I guess this works because after binfmt
fails to detect any elf-binary or shebang, bash executes it in the current context, which is actually bash.