(See the next iteration.)
From time to time, while working with a command line in *nix family of operating systems, we have to write those scripts doing a rather specific task. Usually we use bash
+ utility programs to do the job. However, I had to ask myself: how to implement such a script in C? After an invocation of such a script, it should perform the following:
- Create a temporary source file \$S\,ドル
- Dump the C code to \$S\,ドル
- Compile \$S\$ to the program \$P\,ドル
- Run \$P\$ passing the arguments to it and caching its exit status,
- Remove \$S\$ and \$P\,ドル
- Return the cached exit status of \$P\$.
Code
#! /bin/bash
# Below, XXXX asks for random characters in order to make a unique file name.
# Mac OSX seems to ignore XXXX, yet Ubuntu requires them.
# Create a temporary file name for the source code:
TMP_SOURCE_FILE="$(mktemp -t sourceXXXX).c"
# Create a temporary file name for the executable file:
TMP_PROGRAM_FILE="$(mktemp -t programXXXX)"
# Write the source code into the temporary source file:
cat > $TMP_SOURCE_FILE <<- END_OF_SOURCE
#include <stdio.h>
int main(int argc, char* argv[])
{
int i;
puts("Hello, world! I am a pseudoportable C program.");
for (i = 1; i < argc; ++i)
{
printf("Argument %d: %s\n", i, argv[i]);
}
return argc - 1;
}
END_OF_SOURCE
# Compile and run:
gcc $TMP_SOURCE_FILE -o $TMP_PROGRAM_FILE
$TMP_PROGRAM_FILE $@
EXIT_STATUS=$?
# Clean the source and the binary:
rm $TMP_SOURCE_FILE
rm $TMP_PROGRAM_FILE
# Delegate the exit status of the C program to calling bash:
exit $EXIT_STATUS
So, what do you think? How can I improve anything? Also, can you come with an example of the situation where this "C script" pattern is more preferable than bash
+ command line utilities, Python, etc.?
-
\$\begingroup\$ Have you considered using tcc instead? It can run C as a script. \$\endgroup\$200_success– 200_success2015年11月27日 10:29:25 +00:00Commented Nov 27, 2015 at 10:29
-
\$\begingroup\$ Never heard about tcc before. However, the aim is to be as "portable" as possible and it seems like I can't assume that the user has tcc installed. \$\endgroup\$coderodde– coderodde2015年11月27日 10:32:38 +00:00Commented Nov 27, 2015 at 10:32
1 Answer 1
It's not guaranteed that $TMP_PROGRAM_FILE
is on the $PATH
, so you need to either set the $PATH
or use an absolute path for $TMP_PROGRAM_FILE
.
You have problems with mktemp
.
-t
option: It means different things on GNU/Linuxmktemp(1)
and Mac OS Xmktemp(1)
.On GNU/Linux:
-t interpret TEMPLATE as a single file name component, relative to a directory: $TMPDIR, if set; else the directory specified via -p; else /tmp [deprecated]
On OS X:
mktemp [-t prefix] template ... mktemp -t prefix -t prefix Generate a template (using the supplied prefix and TMPDIR if set) to create a filename tem-plate. template. plate.
Perhaps the confusion is the reason why the GNU/Linux man page indicates that it is deprecated. I think you just want to run
mktemp TEMPLATE
without the-t
option.- Cleanup: Use
trap "rm $TMP_SOURCE_FILE $TMP_PROGRAM_FILE" EXIT
as a more robust way to delete the temporary files, in case your script aborts before reaching therm
commands. .c
suffix: By writingTMP_SOURCE_FILE="$(mktemp -t sourceXXXX).c"
, you end up gettingmktemp
to create the filesourceXXXX
. That file does not get cleaned up. Also, when you docat > $TMP_SOURCE_FILE
, you may be creatingsourceXXXX.c
, or worse, truncating an existing file.On GNU/Linux, a remedy would be to use the
--suffix .c
option. Unfortunately, the BSD/OS X version doesn't support it. One workaround is to rename the file after creation usingmv -i
— which might fail, but at least it's secure.A better workaround would be to use
gcc -x c
to tell GCC that it is C source code, without considering the filename extension.An even better solution would be...
Avoid
$TMP_SOURCE_FILE
altogether:gcc
can take its source code from its standard input! Use-
as the filename.gcc -o "$TMP_PROGRAM_FILE" -x c - <<- END_OF_SOURCE ... END_OF_SOURCE
-
\$\begingroup\$ For some reason I can't read the exit status of the embedded C program after the trap command. Tried on Ubuntu and OSX; both return systematically 0 when the number of arguments should be returned. \$\endgroup\$coderodde– coderodde2015年11月28日 09:13:54 +00:00Commented Nov 28, 2015 at 9:13
-
1\$\begingroup\$ Interesting. Apparently, you need to have the trap handler propagate the exit status explicitly. \$\endgroup\$200_success– 200_success2015年11月28日 09:24:48 +00:00Commented Nov 28, 2015 at 9:24