Skip to content

Instantly share code, notes, and snippets.

@JCookTW
Forked from joehillen/build.sh
Created May 30, 2024 07:12
Show Gist options
  • Select an option

  • Save JCookTW/d8aea20230f1ad9d32c02f1c3ea0e70e to your computer and use it in GitHub Desktop.

Select an option

Save JCookTW/d8aea20230f1ad9d32c02f1c3ea0e70e to your computer and use it in GitHub Desktop.
Build bash scripts with `source` files into a single script.
#!/usr/bin/env bash
#
# https://gist.github.com/joehillen/30f08738c1c3c0ca3e4c754ad33ad2ff
#
# This script inlines 'source' files.
#
# For long scripts, it is nice to be able to break them into multiple files
# to make them easier to work with but still release as a single script.
#
# Inspired by https://stackoverflow.com/a/37533160/334632 with the following enhancements:
# - supports sourcing files with quotes, spaces, and ~.
# - supports sources in sources
# - inline scripts from $PATH
# - errors on (mutual) recursion
# - preserves indentation
# - omits shebangs in sourced files
#
# This will NOT work with variables in source paths.
#
# WARNING MacOS users: Requires a modern version of Bash
set -e
set -o pipefail
declare -A sourced
function _inline_sources {
while IFS='' read -r line; do
if [[ $line =~ ^([[:space:]]*)(source|\.)[[:space:]]+(.+) ]]; then
indent=${BASH_REMATCH[1]}
source_command=${BASH_REMATCH[2]}
file=${BASH_REMATCH[3]}
echo "${indent}# $source_command $file"
if [[ $file != */* ]]; then
fp=$(type -p "$file" || :) # look in $PATH
fi
if [[ -z $fp ]]; then
fp=$(eval realpath "$file") # resolve links, relative paths, ~, quotes, and escapes.
fi
# fail if we've already sourced this file
if [[ ${sourced[$fp]} = 1 ]]; then
echo "ERROR: Recursion detected: $fp"
exit 1
fi
sourced["$fp"]=1
_inline_sources "$fp" | sed -e "/^#!.*/d;s/^/${indent}/" # remove shebang and add indentation
sourced["$fp"]=0
continue
fi
echo "$line"
done < "$1"
}
_inline_sources "$1"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment