Bash script - compile and run c++ code for coding competitions
#!/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 &quot;$1&quot; 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 &quot;Please choose an input file&quot;


echo -e isn't portable, and isn't necessary here anyway.

FILE=&quot;$1&quot;
FILE_IN=&quot;${FILE%.*}.in&quot;


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 &quot;\033[32mCompiling...\033[0m&quot;


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=&quot;-Wuninitialized -Wmaybe-uninitialized&quot;


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


&quot;$FILE&quot; here, too.  I'd write &quot;$TMPFILE&quot; 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 &quot;\033[32mRunning...\033[0m&quot;


tput again.

  if [ -f $FILE_IN ]; then


Quotes again.

    $TMPFILE &lt; $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&gt; /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 &quot;$1&quot; ]
then
  echo &quot;Usage: $0 SOURCE&quot;
  exit 1
fi

executable=$(mktemp -t run-cpp.XXXXXXXXXX)
trap 'rm $executable' EXIT

green=$(tput setaf 2)
normal=$(tput sgr0)

echo &quot;${green}Compiling...${normal}&quot;
g++ -o &quot;$executable&quot; -std=c++17 -O3                     \
    -Wall -Wextra -Wwrite-strings -Wno-parentheses      \
    -Wpedantic -Warray-bounds -Weffc++                  \
    &quot;$1&quot;

echo &quot;${green}Running...${normal}&quot;
input=${1%.*}.in
if [ -f &quot;$input&quot; ]
then exec &lt;&quot;$input&quot;
fi

&quot;$executable&quot;

You might also be interested in my approach to a similar situation, which uses Make rather than shell.