Properly source modules

This commit is contained in:
Manuele Sarfatti 2025-05-11 13:37:25 +02:00
parent cdbc191d7b
commit cb53800a20
15 changed files with 114 additions and 397 deletions

View file

@ -1,6 +1,6 @@
# Beddu build Makefile # Beddu build Makefile
OUT_DIR = build OUT_DIR = dist
OUTPUT = $(OUT_DIR)/beddu.sh OUTPUT = $(OUT_DIR)/beddu.sh
SRC_DIR = src SRC_DIR = src
DEMO_DIR = demo DEMO_DIR = demo
@ -14,7 +14,7 @@ get_dir_files = $(wildcard $(1)*.sh)
# Build ALL_SRC_FILES by including files from each subdirectory in order # Build ALL_SRC_FILES by including files from each subdirectory in order
ALL_SRC_FILES = $(foreach dir,$(SUBDIRS),$(call get_dir_files,$(dir))) ALL_SRC_FILES = $(foreach dir,$(SUBDIRS),$(call get_dir_files,$(dir)))
.PHONY: all clean demo build .PHONY: all clean demo build release
all: $(OUTPUT) all: $(OUTPUT)
@ -25,6 +25,26 @@ build:
demo: build demo: build
@./$(DEMO_DIR)/demo.sh @./$(DEMO_DIR)/demo.sh
release:
@if [ -z "$(filter-out $@,$(MAKECMDGOALS))" ]; then \
echo "Error: Please specify a version number (e.g. make release v0.0.5)"; \
exit 1; \
fi
@VERSION="$(filter-out $@,$(MAKECMDGOALS))"; \
if ! git diff-index --quiet HEAD --; then \
echo "Error: Git working directory is not clean. Please commit or stash your changes first."; \
exit 1; \
fi; \
$(MAKE) build; \
sed -i '' "s/# Version: .*/# Version: $$VERSION/" $(OUTPUT); \
git add $(OUTPUT); \
git commit -m "Release $$VERSION"; \
git tag -a "$$VERSION" -m "Release $$VERSION"
@echo "\nRelease complete: \033[32m$$VERSION\033[0m"
%:
@:
$(OUTPUT): $(ALL_SRC_FILES) $(OUTPUT): $(ALL_SRC_FILES)
@mkdir -p $(OUT_DIR) @mkdir -p $(OUT_DIR)
@echo '#!/usr/bin/env bash' > $(OUTPUT) @echo '#!/usr/bin/env bash' > $(OUTPUT)
@ -33,12 +53,12 @@ $(OUTPUT): $(ALL_SRC_FILES)
@echo '# beddu.sh - A lightweight bash framework for interactive scripts and pretty output' >> $(OUTPUT) @echo '# beddu.sh - A lightweight bash framework for interactive scripts and pretty output' >> $(OUTPUT)
@echo '# https://github.com/mjsarfatti/beddu' >> $(OUTPUT) @echo '# https://github.com/mjsarfatti/beddu' >> $(OUTPUT)
@echo '#' >> $(OUTPUT) @echo '#' >> $(OUTPUT)
@echo '# Version: $(shell git describe --tags)' >> $(OUTPUT) @echo '# Version: $(shell git describe --tags --dirty)' >> $(OUTPUT)
@echo '# Generated on: $(shell date)' >> $(OUTPUT) @echo '# Generated on: $(shell date)' >> $(OUTPUT)
@# Process each file, stripping comments and empty lines @# Process each file, stripping comments, empty lines, and source lines
@for file in $(ALL_SRC_FILES); do \ @for file in $(ALL_SRC_FILES); do \
echo "" >> $(OUTPUT); \ echo "" >> $(OUTPUT); \
grep -v '^\s*#' "$$file" | sed '/^[[:space:]]*$$/d' | sed 's/#[a-zA-Z0-9 ]*$$//' >> $(OUTPUT); \ grep -v '^\s*#\|^source \|^SCRIPT_DIR=\|^readonly BEDDU_.*_LOADED\|^\[\[ \$$BEDDU_.*_LOADED \]\]' "$$file" | sed '/^[[:space:]]*$$/d' | sed 's/#[a-zA-Z0-9 ]*$$//' >> $(OUTPUT); \
done done
@chmod +x $(OUTPUT) @chmod +x $(OUTPUT)
@echo "\nBuild complete: \033[32m$(OUTPUT)\033[0m" @echo "\nBuild complete: \033[32m$(OUTPUT)\033[0m"

View file

@ -1,320 +0,0 @@
#!/usr/bin/env bash
# shellcheck disable=all
#
# beddu.sh - A lightweight bash framework for interactive scripts and pretty output
# https://github.com/mjsarfatti/beddu
#
# Version: v0.0.4
# Generated on: Sun May 11 01:44:23 CEST 2025
readonly _q='?'
readonly _a=''
readonly _o='◌'
readonly _O='●'
readonly _mark='✓'
readonly _warn='!'
readonly _cross='✗'
readonly _spinner='⣷⣯⣟⡿⢿⣻⣽⣾' # See for alternatives: https://antofthy.gitlab.io/info/ascii/Spinners.txt
export _q _a _o _O _mark _warn _cross _spinner
up() {
printf "\033[A"
}
down() {
printf "\033[B"
}
bol() {
printf "\r"
}
eol() {
printf "\033[999C"
}
cl() {
printf "\033[2K"
}
upclear() {
up; bol; cl
}
line() {
printf "\n"
}
show_cursor() {
printf "\033[?25h"
}
hide_cursor() {
printf "\033[?25l"
}
export -f up down bol eol cl line show_cursor hide_cursor
pen() {
local new_line="\n"
local text="${*: -1}"
local args=("${@:1:$#-1}")
local format_code=""
local reset_code="\033[0m"
for arg in "${args[@]}"; do
arg=${arg,,}
case "$arg" in
-n) new_line="" ;;
bold) format_code+="\033[1m" ;;
italic) format_code+="\033[3m" ;;
underline) format_code+="\033[4m" ;;
black) format_code+="\033[30m" ;;
red) format_code+="\033[31m" ;;
green) format_code+="\033[32m" ;;
yellow) format_code+="\033[33m" ;;
blue) format_code+="\033[34m" ;;
purple) format_code+="\033[35m" ;;
cyan) format_code+="\033[36m" ;;
white) format_code+="\033[37m" ;;
grey | gray) format_code+="\033[90m" ;;
[0-9]*)
if [[ "$arg" =~ ^[0-9]+$ ]] && [ "$arg" -ge 0 ] && [ "$arg" -le 255 ]; then
format_code+="\033[38;5;${arg}m"
fi
;;
*) ;;
esac
done
printf "%b%s%b%b" "${format_code}" "${text}" "${reset_code}" "${new_line}"
}
export -f pen
run() {
local outvar_name errvar_name
local -n outvar errvar # Declare namerefs (will be assigned below if needed)
local cmd
while [[ $# -gt 0 ]]; do
case "$1" in
--out)
outvar_name="$2"
shift 2
;;
--err)
errvar_name="$2"
shift 2
;;
*)
cmd=("$@")
break
;;
esac
done
[[ -n "${outvar_name}" ]] && local -n outvar="${outvar_name}"
[[ -n "${errvar_name}" ]] && local -n errvar="${errvar_name}"
local stdout_file stderr_file
stdout_file=$(mktemp)
stderr_file=$(mktemp)
"${cmd[@]}" >"${stdout_file}" 2>"${stderr_file}"
local exit_code=$?
[[ -n "${outvar_name}" ]] && outvar="$(<"$stdout_file")"
[[ -n "${errvar_name}" ]] && errvar="$(<"$stderr_file")"
rm -f "${stdout_file}" "${stderr_file}"
return $exit_code
}
export -f run
check() {
if spinning; then
spop
upclear
fi
pen -n green "${_mark:-} "
pen "$@"
}
export -f check
repen() {
upclear
pen "$@"
}
export -f repen
trap spop EXIT INT TERM
_spinner_frame_duration=0.1
_spinner_pid=""
spin() {
local message=("$@")
_spinner="${_spinner:-⣷⣯⣟⡿⢿⣻⣽⣾}"
if spinning; then
spop --keep-cursor-hidden
fi
(
hide_cursor
trap "show_cursor; exit 0" USR1
pen -n cyan "${_spinner:0:1} "
pen "${message[@]}"
while true; do
for ((i = 0; i < ${#_spinner}; i++)); do
frame="${_spinner:$i:1}"
up
bol
pen -n cyan "${frame} "
pen "${message[@]}"
sleep $_spinner_frame_duration
done
done
) &
_spinner_pid=$!
}
spop() {
local keep_cursor_hidden=false
[[ "$1" == "--keep-cursor-hidden" ]] && keep_cursor_hidden=true
if spinning; then
kill -USR1 "${_spinner_pid}" 2>/dev/null
sleep $_spinner_frame_duration
if ps -p "${_spinner_pid}" >/dev/null 2>&1; then
kill "${_spinner_pid}" 2>/dev/null
if [[ "$keep_cursor_hidden" == false ]]; then
show_cursor
fi
fi
_spinner_pid=""
fi
}
spinning() {
[[ -n "${_spinner_pid}" ]]
}
export -f spin spop spinning
throw() {
if spinning; then
spop
upclear
fi
pen -n red "${_cross:-} "
pen "$@"
}
export -f throw
warn() {
if spinning; then
spop
upclear
fi
pen -n yellow bold italic "${_warn:-!} "
pen italic "$@"
}
export -f warn
ask() {
local -n outvar="$1"
local prompt
local answer
prompt=$(
pen -n blue "${_q:-?} "
pen "${2}"
pen -n blue "${_a:-} "
)
show_cursor
while true; do
read -r -p "$prompt" answer
case "$answer" in
"")
echo
warn "Please type your answer."
;;
*) break ;;
esac
done
outvar="$answer"
}
export -f ask
choose() {
local -n outvar="$1"
local prompt
local options=("${@:3}")
local current=0
local count=${#options[@]}
prompt=$(
pen -n blue "${_q:-?} "
pen -n "${2}"
pen gray "[↑↓]"
)
hide_cursor
trap 'show_cursor; return' INT TERM
pen "$prompt"
while true; do
local index=0
for item in "${options[@]}"; do
if ((index == current)); then
pen -n blue "${_O:-} "
pen "${item}"
else
pen gray "${_o:-} ${item}"
fi
((index++))
done
read -s -r -n1 key
if [[ $key == $'\e' ]]; then
read -s -r -n2 -t 0.0001 escape
key+="$escape"
fi
case "$key" in
$'\e[A' | 'k')
((current--))
[[ $current -lt 0 ]] && current=$((count - 1))
;;
$'\e[B' | 'j')
((current++))
[[ $current -ge "$count" ]] && current=0
;;
'')
break
;;
esac
echo -en "\e[${count}A\e[J"
done
outvar="${options[$current]}"
}
confirm() {
local default="y"
local hint="[Y/n]"
local prompt
local response
while [[ $# -gt 0 ]]; do
case "$1" in
--default-no)
default="n"
hint="[y/N]"
shift
;;
--default-yes)
shift
;;
*) break ;;
esac
done
prompt=$(
pen -n blue "${_q:-?} "
pen -n "$1"
pen gray " $hint"
pen -n blue "${_a:-} "
)
show_cursor
while true; do
read -r -p "$prompt" response
response="${response:-$default}"
case "$response" in
[Yy] | [Yy][Ee][Ss])
upclear
pen -n blue "${_a:-} "
pen "yes"
return 0
;;
[Nn] | [Nn][Oo])
upclear
pen -n blue "${_a:-} "
pen "no"
return 1
;;
*)
echo
warn "Please answer yes or no."
;;
esac
done
}
export -f confirm

View file

@ -9,6 +9,8 @@ demo() {
_violet=99 _violet=99
_pink=219 _pink=219
pen "\n>> $_q <<\n"
line line
pen $_violet "╔═════════════════════════════════════════════╗" pen $_violet "╔═════════════════════════════════════════════╗"
pen $_violet "║ ║" pen $_violet "║ ║"

View file

@ -1,6 +1,9 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# @private # @private
[[ $BEDDU_SYMBOLS_LOADED ]] && return
readonly BEDDU_SYMBOLS_LOADED=true
readonly _q='?' readonly _q='?'
readonly _a='' readonly _a=''
readonly _o='◌' readonly _o='◌'
@ -9,5 +12,4 @@ readonly _mark='✓'
readonly _warn='!' readonly _warn='!'
readonly _cross='✗' readonly _cross='✗'
readonly _spinner='⣷⣯⣟⡿⢿⣻⣽⣾' # See for alternatives: https://antofthy.gitlab.io/info/ascii/Spinners.txt readonly _spinner='⣷⣯⣟⡿⢿⣻⣽⣾' # See for alternatives: https://antofthy.gitlab.io/info/ascii/Spinners.txt
readonly _spinner_frame_duration=0.1
export _q _a _o _O _mark _warn _cross _spinner

View file

@ -1,6 +1,9 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# movements.sh - Cursor helper functions # movements.sh - Cursor helper functions
[[ $BEDDU_MOVEMENTS_LOADED ]] && return
readonly BEDDU_MOVEMENTS_LOADED=true
# Move cursor up one line # Move cursor up one line
up() { up() {
printf "\033[A" printf "\033[A"
@ -45,6 +48,3 @@ show_cursor() {
hide_cursor() { hide_cursor() {
printf "\033[?25l" printf "\033[?25l"
} }
# Export the functions so they are available to subshells
export -f up down bol eol cl line show_cursor hide_cursor

View file

@ -1,8 +1,8 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# pen.sh - Print pretty text # pen.sh - Print pretty text
# @depends on: [[ $BEDDU_PEN_LOADED ]] && return
# - _symbols.sh readonly BEDDU_PEN_LOADED=true
# Print text with ANSI color codes and text formatting # Print text with ANSI color codes and text formatting
# #
@ -57,6 +57,3 @@ pen() {
printf "%b%s%b%b" "${format_code}" "${text}" "${reset_code}" "${new_line}" printf "%b%s%b%b" "${format_code}" "${text}" "${reset_code}" "${new_line}"
} }
# Export the pen function so it's available to subshells
export -f pen

View file

@ -1,6 +1,9 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# run.sh - Execute commands with output/error capture # run.sh - Execute commands with output/error capture
[[ $BEDDU_RUN_LOADED ]] && return
readonly BEDDU_RUN_LOADED=true
# Execute a command with stdout and stderr capture capabilities # Execute a command with stdout and stderr capture capabilities
# #
# Usage: # Usage:
@ -54,6 +57,3 @@ run() {
rm -f "${stdout_file}" "${stderr_file}" rm -f "${stdout_file}" "${stderr_file}"
return $exit_code return $exit_code
} }
# Export the run function so it's available to subshells
export -f run

View file

@ -1,10 +1,15 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# shellcheck disable=SC1091
# check.sh - Print a success message # check.sh - Print a success message
# @depends on: [[ $BEDDU_CHECK_LOADED ]] && return
# - pen.sh readonly BEDDU_CHECK_LOADED=true
# - movements.sh
# - _symbols.sh SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")"
source "$SCRIPT_DIR/../00.utils/_symbols.sh"
source "$SCRIPT_DIR/../00.utils/movements.sh"
source "$SCRIPT_DIR/../01.core/pen.sh"
source "$SCRIPT_DIR/spin.sh"
# Print a checkmark with a message, and stop and replace the # Print a checkmark with a message, and stop and replace the
# spinner if it's running (relies on the spinner being the last # spinner if it's running (relies on the spinner being the last
@ -31,6 +36,3 @@ check() {
pen -n green "${_mark:-} " pen -n green "${_mark:-} "
pen "$@" pen "$@"
} }
# Export the check function so it can be used in other scripts
export -f check

View file

@ -1,9 +1,13 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# shellcheck disable=SC1091
# repen.sh - Overwrite the previous line with new text # repen.sh - Overwrite the previous line with new text
# @depends on: [[ $BEDDU_REPEN_LOADED ]] && return
# - pen.sh readonly BEDDU_REPEN_LOADED=true
# - movements.sh
SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")"
source "$SCRIPT_DIR/../00.utils/movements.sh"
source "$SCRIPT_DIR/../01.core/pen.sh"
# Move up one line, move to the beginning, clear the line, and print the text. # Move up one line, move to the beginning, clear the line, and print the text.
# #
@ -20,6 +24,3 @@ repen() {
upclear upclear
pen "$@" pen "$@"
} }
# Export the repen function so it can be used in other scripts
export -f repen

View file

@ -1,17 +1,21 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# shellcheck disable=SC1091
# spin.sh - Print a spinner with a message # spin.sh - Print a spinner with a message
# @depends on: [[ $BEDDU_SPIN_LOADED ]] && return
# - pen.sh readonly BEDDU_SPIN_LOADED=true
# - movements.sh
# - _symbols.sh SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")"
source "$SCRIPT_DIR/../00.utils/_symbols.sh"
source "$SCRIPT_DIR/../00.utils/movements.sh"
source "$SCRIPT_DIR/../01.core/pen.sh"
# Make sure the cursor is shown and the spinner stopped if the script exits abnormally # Make sure the cursor is shown and the spinner stopped if the script exits abnormally
trap spop EXIT INT TERM trap spop EXIT INT TERM
# Module state variables # Module state variables
_spinner_frame_duration=0.1
_spinner_pid="" _spinner_pid=""
_frame_duration="${_spinner_frame_duration:-0.1}"
# Print a message with a spinner at the beginning # Print a message with a spinner at the beginning
# #
@ -30,7 +34,7 @@ _spinner_pid=""
# check "Dependancies installed." # check "Dependancies installed."
spin() { spin() {
local message=("$@") local message=("$@")
_spinner="${_spinner:-⣷⣯⣟⡿⢿⣻⣽⣾}" local spinner="${_spinner:-⣷⣯⣟⡿⢿⣻⣽⣾}"
# If there is already a spinner running, stop it # If there is already a spinner running, stop it
if spinning; then if spinning; then
@ -45,17 +49,17 @@ spin() {
trap "show_cursor; exit 0" USR1 trap "show_cursor; exit 0" USR1
# Print the first frame of the spinner # Print the first frame of the spinner
pen -n cyan "${_spinner:0:1} " pen -n cyan "${spinner:0:1} "
pen "${message[@]}" pen "${message[@]}"
while true; do while true; do
for ((i = 0; i < ${#_spinner}; i++)); do for ((i = 0; i < ${#spinner}; i++)); do
frame="${_spinner:$i:1}" frame="${spinner:$i:1}"
up up
bol bol
pen -n cyan "${frame} " pen -n cyan "${frame} "
pen "${message[@]}" pen "${message[@]}"
sleep $_spinner_frame_duration sleep "$_frame_duration"
done done
done done
) & ) &
@ -73,7 +77,7 @@ spop() {
kill -USR1 "${_spinner_pid}" 2>/dev/null kill -USR1 "${_spinner_pid}" 2>/dev/null
# Wait briefly for cleanup # Wait briefly for cleanup
sleep $_spinner_frame_duration sleep "$_frame_duration"
# Ensure it's really gone # Ensure it's really gone
if ps -p "${_spinner_pid}" >/dev/null 2>&1; then if ps -p "${_spinner_pid}" >/dev/null 2>&1; then
@ -92,6 +96,3 @@ spop() {
spinning() { spinning() {
[[ -n "${_spinner_pid}" ]] [[ -n "${_spinner_pid}" ]]
} }
# Export the functions so they are available to subshells
export -f spin spop spinning

View file

@ -1,10 +1,15 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# shellcheck disable=SC1091
# throw.sh - Print an throw message # throw.sh - Print an throw message
# @depends on: [[ $BEDDU_THROW_LOADED ]] && return
# - pen.sh readonly BEDDU_THROW_LOADED=true
# - movements.sh
# - _symbols.sh SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")"
source "$SCRIPT_DIR/../00.utils/_symbols.sh"
source "$SCRIPT_DIR/../00.utils/movements.sh"
source "$SCRIPT_DIR/../01.core/pen.sh"
source "$SCRIPT_DIR/spin.sh"
# Print an throwmark with a message, and stop and replace the # Print an throwmark with a message, and stop and replace the
# spinner if it's running (relies on the spinner being the last # spinner if it's running (relies on the spinner being the last
@ -31,6 +36,3 @@ throw() {
pen -n red "${_cross:-} " pen -n red "${_cross:-} "
pen "$@" pen "$@"
} }
# Export the throw function so it can be used in other scripts
export -f throw

View file

@ -1,10 +1,15 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# shellcheck disable=SC1091
# warn.sh - Print a warning message # warn.sh - Print a warning message
# @depends on: [[ $BEDDU_WARN_LOADED ]] && return
# - pen.sh readonly BEDDU_WARN_LOADED=true
# - movements.sh
# - _symbols.sh SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")"
source "$SCRIPT_DIR/../00.utils/_symbols.sh"
source "$SCRIPT_DIR/../00.utils/movements.sh"
source "$SCRIPT_DIR/../01.core/pen.sh"
source "$SCRIPT_DIR/spin.sh"
# Print a "!" with a message, and stop and replace the # Print a "!" with a message, and stop and replace the
# spinner if it's running (relies on the spinner being # spinner if it's running (relies on the spinner being
@ -31,6 +36,3 @@ warn() {
pen -n yellow bold italic "${_warn:-!} " pen -n yellow bold italic "${_warn:-!} "
pen italic "$@" pen italic "$@"
} }
# Export the warn function so it can be used in other scripts
export -f warn

View file

@ -1,10 +1,15 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# shellcheck disable=SC1091
# ask.sh - Get free text input from the user # ask.sh - Get free text input from the user
# @depends on: [[ $BEDDU_ASK_LOADED ]] && return
# - pen.sh readonly BEDDU_ASK_LOADED=true
# - _symbols.sh
# - cursor.sh SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")"
source "$SCRIPT_DIR/../00.utils/_symbols.sh"
source "$SCRIPT_DIR/../00.utils/movements.sh"
source "$SCRIPT_DIR/../01.core/pen.sh"
source "$SCRIPT_DIR/../02.ui/warn.sh"
# Ask a question and get a free text answer from the user # Ask a question and get a free text answer from the user
# #
@ -42,6 +47,3 @@ ask() {
# shellcheck disable=SC2034 # shellcheck disable=SC2034
outvar="$answer" outvar="$answer"
} }
# Export the ask function so it's available to subshells
export -f ask

View file

@ -1,10 +1,14 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# shellcheck disable=SC1091
# choose.sh - Choose from a menu of options # choose.sh - Choose from a menu of options
# @depends on: [[ $BEDDU_CHOOSE_LOADED ]] && return
# - pen.sh readonly BEDDU_CHOOSE_LOADED=true
# - _symbols.sh
# - cursor.sh SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")"
source "$SCRIPT_DIR/../00.utils/_symbols.sh"
source "$SCRIPT_DIR/../00.utils/movements.sh"
source "$SCRIPT_DIR/../01.core/pen.sh"
# Print an interactive menu of options and return the selected option # Print an interactive menu of options and return the selected option
# #

View file

@ -1,10 +1,15 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# shellcheck disable=SC1091
# confirm.sh - Read a yes/no confirmation from the user # confirm.sh - Read a yes/no confirmation from the user
# @depends on: [[ $BEDDU_CONFIRM_LOADED ]] && return
# - pen.sh readonly BEDDU_CONFIRM_LOADED=true
# - _symbols.sh
# - movements.sh SCRIPT_DIR="$(dirname -- "${BASH_SOURCE[0]}")"
source "$SCRIPT_DIR/../00.utils/_symbols.sh"
source "$SCRIPT_DIR/../00.utils/movements.sh"
source "$SCRIPT_DIR/../01.core/pen.sh"
source "$SCRIPT_DIR/../02.ui/warn.sh"
# Ask a question and get a yes/no answer from the user # Ask a question and get a yes/no answer from the user
# #
@ -74,6 +79,3 @@ confirm() {
esac esac
done done
} }
# Export the confirm function so it's available to subshells
export -f confirm