Skip to content

Instantly share code, notes, and snippets.

@Urpagin
Created November 30, 2025 03:40
Show Gist options
  • Select an option

  • Save Urpagin/551fa3efa77d56d1ca701efd391ea304 to your computer and use it in GitHub Desktop.

Select an option

Save Urpagin/551fa3efa77d56d1ca701efd391ea304 to your computer and use it in GitHub Desktop.
A Bash script to quickly build & execute a C project.
#!/usr/bin/env bash
# Author: Urpagin
# Date: 2025-11-30
# Description: eXecute C (xc) compiles and executes a C project
# without cluttering your machine.
set -euo pipefail
_usage() {
cat << 'SKIBIDI'
DESCRIPTION
eXecute C (xc) compiles and executes C code
without leaving any clutter (out files) on
your machine.
The build is made using debug arguments.
USAGE
xc [C_SOURCE_FILE_OR_OPTION...] -- [ARG...]
EXAMPLE
# Standard usage
xc main.c
# Multiple source files
xc main.c lib1.c lib2.c
# Multiple source files & three arguments
xc main.c lib.c -- John Arkansas 22
# You can override the compiler flags
XC_CFLAGS='-Wall -O3' xc main.c
# This is also valid
XC_CFLAGS='' xc -Wall -O3 main.c
BEHAVIOUR
The default compiler is clang, if not found, gcc
is used.
SKIBIDI
}
if [[ "${1:-}" == '-h' || "${1:-}" == '--help' ]]; then
_usage
exit 0
fi
# We prefer clang because it generates better error messages.
_get_compiler() {
if command -v clang >/dev/null 2>&1; then
echo 'clang'
return 0
fi
if command -v gcc >/dev/null 2>&1; then
echo 'gcc'
return 0
fi
# No compatible compiler found
echo 'No compatible C compiler found (need clang or gcc)' >&2
return 1
}
_COMPILER="$(_get_compiler)" || exit 1
readonly _COMPILER
declare -ra _DEFAULT_C_FLAGS=(
'-std=c11'
'-Wall'
'-Wextra'
'-Wpedantic'
'-Og'
'-g'
)
# Use user-provided XC_CFLAGS in priority if provided.
_C_FLAGS=()
if [[ "${XC_CFLAGS+x}" ]]; then
# splits XC_CFLAGS into an array without globbing
read -r -a _C_FLAGS <<< "${XC_CFLAGS}"
else
_C_FLAGS=("${_DEFAULT_C_FLAGS[@]}")
fi
readonly -a _C_FLAGS
if [[ -z "${1:-}" ]]; then
printf 'Missing C source files\nUse -h to show detailed help\n'
exit 1
fi
# Temp directory where to store our out file.
_TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}"/xc.XXXXXXXXXXXX)"
readonly _BIN_PATH="${_TMP_DIR}/xc_a.out"
readonly _TMP_DIR
_cleanup() {
# Remove temp directory
[[ "$_TMP_DIR" == "${TMPDIR:-/tmp}"* ]] && rm -rf -- "$_TMP_DIR"
}
trap _cleanup 'EXIT'
# Separate compiler flags from program flags.
# -- is the delimiter.
compiler_args=()
program_args=()
program_flag=false
for arg in "$@"; do
[[ "$arg" == '--' ]] && program_flag=true && continue
if [[ "$program_flag" == false ]]; then
compiler_args+=("$arg")
else
program_args+=("$arg")
fi
done
# Compile. Arguments for the compiler.
"$_COMPILER" "${_C_FLAGS[@]}" -o "$_BIN_PATH" "${compiler_args[@]}"
# Execute
"$_BIN_PATH" "${program_args[@]}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment