You have struggled for a few hours and put something together, but what happens if the compilation fails? What happens if there are no arguments? What happens if there are 4 arguments for the program?
When writing a script, or a program, the most important thing you can do is validate every necessary step! If you are expecting input, validate you received it. Once you have the input, validate it is what your expected. Is it a value within the usable range? Is it a filename that meets my requirements? Does it exist? Did my compile succeed or fail? Do I try and execute the program?
All of these need to be addressed. Otherwise, your script (or program) will stray off on some undefined path.
Bash provides a wealth of conditional expressions that can be used with test
(or [...]
) or using the bash [[...]]
operator. See Bash Manual - 6.4 Bash Conditional Expressions. Make use of the language features to make your code robust.
Bash provides a wealth of string handling features through parameter expansions Bash Manual - 3.5.3 Shell Parameter Expansion, use them to check the extension to make sure it is c, cpp or c++
Putting those features together with a few if...then...fi
statements will make your script much more reliable -- and actually do what it is you are trying to do.
The following short script takes arguments (bash Positional Parameters) requiring that at least the filename to compile be provided, and passing any additional arguments to the compiled program as command line arguments with ${@:2}
(all bash positional parameters beginning with the 2nd one)
#!/bin/bash
if [ -z "$1" ] ## validate at least one argument given
then
printf "error: insufficient arguments\n" >&2
printf "usage: %s file.cpp [args]\n" "${0##*/}" >&2
exit 1
fi
if [ ! -r "$1" ] ## validate file is readable
then
printf "error: file not found '%s'\n" "$1" >&2
exit 1
fi
src="$1" ## give source file a handy name
## check the source file ends in '.c', '.cpp' or '.c++'
if [ "${src##*.}" != 'c' -a "${src##*.}" != 'cpp' -a "${src##*.}" != 'c++' ]
then
printf "error: first argument not a c/c++ file\n" >&2
exit 1
fi
ext="${src##*.}" ## save the extension
if [ "${#ext}" = 'c' ] ## check if ext is 'c' use gcc else use g++
then
## always enable compiler warnings, -Wall -Wextra -pedantic, minimum
# -Wshadow to catch shadowed variables, -Werror treat warnings as error
gcc -Wall -Wextra -pedantic -Wshadow -Werror \
-std=c11 -O3 -o "${src%.*}" "$src"
else
g++ -Wall -Wextra -pedantic -Wshadow -Werror \
-std=c++11 -O3 -o "${src%.*}" "$src"
fi
if [ $? -eq '0' ] ## check the compiler return, run only on success
then
./"${src%.*}" ${@:2}
else
printf "\nAn error occurred, executable not called\n\n" >&2
fi
Now a couple of short examples to make sure it works:
#include <stdio.h>
int main (int argc, char **argv) {
const char *s = "hello c file.";
printf ("%s\n", s);
for (int i = 1; i < argc; i++)
printf ("arg[%d]: %s\n", i, argv[i]);
}
Example Use/Output
$ bash compilewargs.sh cfile.c foo bar baz
hello c file.
arg[1]: foo
arg[2]: bar
arg[3]: baz
For C++
#include <iostream>
#include <string>
int main (int argc, char **argv) {
std::string s = "hello cpp file.";
std::cout << s << '\n';
for (int i = 1; i < argc; i++)
std::cout << "arg[" << i << "]: " << argv[i] << '\n';
}
Example Use/Output
$ bash compilewargs.sh cppfile.cpp foo bar baz
hello cpp file.
arg[1]: foo
arg[2]: bar
arg[3]: baz
Does the error-handling work?
#include <stdio.h>
int main (void) {
const char *s = "hello c file.";
printf ("%s\n", unknown);
}
Example Use/Output
$ bash compilewargs.sh cerrfile.c foo bar baz
cerrfile.c: In function ‘int main()’:
cerrfile.c:7:21: error: ‘unknown’ was not declared in this scope
printf ("%s\n", unknown);
^
cerrfile.c:5:17: error: unused variable ‘s’ [-Werror=unused-variable]
const char *s = "hello c file.";
^
cc1plus: all warnings being treated as errors
An error occurred, executable not called
What about a file that doesn't exist?
$ bash compilewargs.sh myfile.c foo bar baz
error: file not found 'myfile.c'
What about a text (or any other non-c, cpp or c++
) file?
$ bash compilewargs.sh compilewargs.sh foo bar baz
error: first argument not a c/c++ file
By taking the time to think though what the dumbest user you know could ever try and do with your script -- and protecting against it, you can write reasonably robust scripts that will save you grief. Look things over and let me know if you have further questions.