Last active
January 24, 2025 22:05
-
-
Save joehillen/30f08738c1c3c0ca3e4c754ad33ad2ff to your computer and use it in GitHub Desktop.
Build bash scripts with `source` files into a single script.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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" |
For anyone who is struggling with this, I've developed a POSIX script that is compatible with ShellCheck and completely customizable: https://github.com/carlocorradini/inline
Hope this is useful π₯³
the script from https://stackoverflow.com/a/37533160 (just wrapped it into a function like yours)
#!/usr/bin/env bash
set -e
set -o pipefail
declare -A sourced
function _inline_sources {
while read line; do
if [[ "$line" =~ (\.|source)\s+.+ ]]; then
file="$(echo $line | cut -d' ' -f2)"
echo "$(cat $file)"
else
echo "$line"
fi
done < "$1"
}
_inline_sources "$1"
sadly your script includes the first sourced file for all further sources.
.
βββ bin
βΒ Β βββ build.sh
βββ lib
βΒ Β βββ example.sh
βΒ Β βββ header.sh
βΒ Β βββ lib-wget.sh
βββ test.sh
test.sh
#!/bin/ash
source lib/header.sh
echo "my script start"
source lib/lib-wget.sh
source lib/example.sh
echo do something
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
PROTIP: Turn an external script in
$PATH
into a function to make it portable: