#!/bin/bash
I don't see why you're using Bash for this - there should be no problem using standard POSIX shell.
if [ -z $1 ]; then
I would recommend "$1"
there, even though extra arguments will cause [
to return false for other reasons. Passing as a single argument won't emit any error messages.
echo -e "Please choose an input file"
echo -e
isn't portable, and isn't necessary here anyway.
FILE="$1"
FILE_IN="${FILE%.*}.in"
Avoid all-caps names for variables in your own program - these are generally reserved for environment variables which change programs' behaviour.
clear
I think that's a bit rude - some of us like to be able to compare results against the previous run. If I want to clear before I run, I can type that easily.
echo -e "\033[32mCompiling...\033[0m"
Don't embed terminal-specific escape codes like that! Even though most recent terminals support the ANSI escapes, there are others - and if you redirect to file, you don't want it littered with control characters. Use tput
to generate the correct escapes for your $TERM
(and slightly more readable code).
TMPFILE=$(mktemp /tmp/run-cpp.XXXXXXXXXX)
That's not a very descriptive variable name. It's more informative to say what it's for (the executable to create). And why hard-code /tmp
as the directory? Prefer $TMPDIR
if set (perhaps systems with per-user temp directories, or with a choice of fast or large temporary storage).
WARNING_FLAGS="-Wuninitialized -Wmaybe-uninitialized"
That's quite a lax set of warnings. If you care about the quality of your source, add a few more. I suggest -Wall -Wextra -Wwrite-strings -Wno-parentheses -Wpedantic -Warray-bounds -Weffc++
. If you only care about performance, then perhaps -Wall -Wextra -Wno-parentheses -Warray-bounds
might be sufficient.
Since we use this variable only once, perhaps we should inline its use, thereby not triggering a Shellcheck warning where we (correctly) expand it without quotes.
g++ $FILE -std=c++17 $WARNING_FLAGS -O3 -o $TMPFILE
"$FILE"
here, too. I'd write "$TMPFILE"
even though we constructed it to be a safe name.
if [ $? -eq 0 ]; then
Anti-pattern - just use the preceding command directly after if
. Or, enable the -e
flag of the shell, to just exit if the compilation fails.
echo -e "\033[32mRunning...\033[0m"
tput
again.
if [ -f $FILE_IN ]; then
Quotes again.
$TMPFILE < $FILE_IN
else
$TMPFILE
fi
As we don't use stdin, we could just redirect stdin
using exec
, and not need two different commands.
ERROR=$?
fi
rm $TMPFILE 2> /dev/null
Why didn't we remove the temporary file if we exited early?
exit $ERROR
We wouldn't need to store the error if we made removing the temp-file an exit trap.
Modified code
#!/bin/sh
# Compiles and runs C++ source code. A corresponding file
# ending with .in will be used as input, if present
set -eu
if [ $# -ne 1 ] || [ -z "$1" ]
then
echo "Usage: $0 SOURCE"
exit 1
fi
executable=$(mktemp -t run-cpp.XXXXXXXXXX)
trap 'rm $executable' EXIT
green=$(tput setaf 2)
normal=$(tput sgr0)
echo "${green}Compiling...${normal}"
g++ -o "$executable" -std=c++17 -O3 \
-Wall -Wextra -Wwrite-strings -Wno-parentheses \
-Wpedantic -Warray-bounds -Weffc++ \
"$1"
echo "${green}Running...${normal}"
input=${1%.*}.in
if [ -f "$input" ]
then exec <"$input"
fi
"$executable"
You might also be interested in my approach to a similar situation, which uses Make rather than shell.