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 hidden or 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; | |
| } |
Author
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 -ewe could just as well giveexit 0there, 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
mktempso 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 -awhich gives ability to setargv[0]explicitly. However/bin/shis oftendashand notbash(like on Debian) so it is not supported.Other way that makes it possible to use
execthat 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
execbefore 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/tmpfilethen in the clean-up subprocess instead ofsleepputread <&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: