Last active
November 13, 2022 21:30
-
-
Save hadrianw/5cd275d5838814d813adc12978dce9be to your computer and use it in GitHub Desktop.
Make a C file executable with those few lines.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#if 0 | |
set -e; [ "$0" -nt "$0.bin" ] && | |
gcc -Wall -Wextra -pedantic -std=c99 "$0" -o "$0.bin" | |
exec "$0.bin" "$@" | |
#endif | |
#include <stdio.h> | |
int | |
main(int argc, char *argv[]) { | |
int ch = getchar(); | |
printf("Hello %c world!\n", ch); | |
return 0; | |
} |
Yeah, it makes sense as you describe the whole behavior.
Basically I thought of TCC's and Dlang's -run
flags; what I know for sure is that DMD would generate an executable at /tmp
, run it, and then delete it immediately.
Of course as you have said yourself, the workarounds you have just shared (thank you by the way) are quite hacky and a bit verbose to say the least, therefore I should stick with your original preprocessor technique.
Thank you for your thorough explanation, I was reminded how script behavior works.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@stefanos82 I think there is no way to make it elegant, but something like this would work:
The problem is, that we need to leave a shell process running for the clean up. Because of that we can't use exec and we must exit ourselves. But thanks to
set -e
we could just as well giveexit 0
there, as in case of errors we would still get a correct exit code from our program.This way is a bit less nice, also because the PID of our process is different from the PID of the command. We could think about putting the binary in /tmp and use first
mktemp
so it could also work from a read-only place like read-only mount or a different user. But I don't like it, because the name (argv[0]
) will be cryptic and we can't deduce from the binary were the sources are and some programs may want to have access to some additional files. A solution to that would beexec -a
which gives ability to setargv[0]
explicitly. However/bin/sh
is oftendash
and notbash
(like on Debian) so it is not supported.Other way that makes it possible to use
exec
that I thought about is more hacky, but maybe there is a way to make it less racy:Shell runs a background process that will wait just a bit and then remove the binary. If the parent process will get to
exec
before this one second will run out everything is good. Because the kernel keeps a file as long as there is a file handle opened to it. In this case a running program keeps a handle. It may not be accessible from the file system, but everything works as intended. I'm not sure what could be done to avoid this racy one second sleep. On the one hand it can be too long, because in less than a second the program could already be done and we wait with removal longer than neccessary. On the other hand it can be too short in a busy system or a slow file system. It would be better for it to wait only as long as neccessary. I wonder if there is a way in shell to have an opened file descriptor with close-on-exec flag set and then the clean-up subprocess would attempt to read from it - then it should break on exec and we would be in the clear.Or rather open a file from parent with
exec 3<> /tmp/tmpfile
then in the clean-up subprocess instead ofsleep
putread <&3
, but I did not test it out too much. Probably should work, but then you should replace /tmp/tmpfile with propermktemp
. But all this will look bad.Not much tested: