ANSWER: ```bash bash /var/tmp/automate/utils/conf_print/conf_print_gen.sh \ --recurse \ -s /space/code_repositories/zfsbootmenu/zfsbootmenu/etc \ -t ${BUILD_DIR} \ -o /tmp/test_zbm_build.sh ``` --- - I have a script that needs to change configuration files in the configuration directory for the zbm-builder.sh from zfsbootmenu. It git clones the zfsbootmenu to /var/tmp/zfsbootmenu and the contents of the etc directory within that repo is what in needed to change. I want to use the following script to create a complete set of function wrapped heredocs to include in my zbm-builder configuration scripts can you give me the arguments to use inorder to match the variable of the zbm-builder configuration script. Here is th heredoc wrapper script: cat conf_print_gen.sh #!/bin/bash # Default values DEFAULT_SOURCE_DIR="scripts" DEFAULT_OUTPUT_FILE="output.txt" DEFAULT_TEE_DEST="\$DEST/.config/qtile/scripts" # Usage message usage() { cat <&2 usage 1 ;; esac done # Validate source directory if [[! -d "$SOURCE_DIR"]]; then echo "Error: Source directory '$SOURCE_DIR' does not exist." >&2 exit 1 fi # Check if source directory is empty shopt -s nullglob files=("$SOURCE_DIR"/\*) shopt -u nullglob if [[${#files[@]} -eq 0]]; then echo "Error: Source directory '$SOURCE_DIR' is empty." >&2 exit 1 fi # Get absolute paths for comparison OUTPUT_DIR=$(cd "$(dirname "$OUTPUT_FILE")" 2>/dev/null && pwd) OUTPUT_FILE_ABS="${OUTPUT_DIR}/$(basename "$OUTPUT_FILE")" # Function to check if file is binary is_binary() { local file="$1" file --mime-encoding "$file" 2>/dev/null | grep -q "binary" } # Function to create safe identifier from filename make*safe_identifier() { local filename="$1" local safe="${filename%.\*}" safe="${safe//[^a-zA-Z0-9]/*}" safe=$(echo "$safe" | sed 's/_\+/_/g; s/^_//; s/_$//') echo "$safe" } # Clear or create output file > "$OUTPUT_FILE" # Counters processed=0 skipped=0 for filepath in "$SOURCE_DIR"/\*; do # Skip if not a regular file [[-f "$filepath"]] || continue # Get absolute path of current file filepath_dir=$(cd "$(dirname "$filepath")" && pwd) filepath_abs="${filepath_dir}/$(basename "$filepath")" # Skip the output file if it is in the source directory if [["$filepath_abs" == "$OUTPUT_FILE_ABS"]]; then echo "Skipping output file: $filepath" ((skipped++)) continue fi # Skip binary files if is_binary "$filepath"; then echo "Skipping binary file: $filepath" ((skipped++)) continue fi # Extract filename and create safe identifier filename=$(basename "$filepath") safe_id=$(make_safe_identifier "$filename") # Create unique heredoc delimiter heredoc*delim="EOF*${safe_id}\_EOF" # Function name func*name="conf_print*${safe_id}" # Write function definition { echo "${func_name}() {" echo "cat <<'${heredoc_delim}'" cat "$filepath" echo "" echo "${heredoc_delim}" echo "}" echo "${func_name} | tee \"${TEE_DEST}/${filename}\"" echo "" } >>"$OUTPUT_FILE" ((processed++)) echo "Processed: $filename -> delimiter: $heredoc_delim" done echo "----------------------------------------" echo "Processing complete!" echo " Processed: $processed file(s)" echo " Skipped: $skipped file(s)" echo " Output: $OUTPUT_FILE" default@sistarte  /space/code_repo And here is my zbm-build configuration script: cat build_zfsbootmenu_docker.sh #!/bin/bash # ZFSBootMenu UKI Builder v3.1.x - Enterprise Production EFI set -euo pipefail show_help() { cat <<'EOF' Usage: sudo script.sh [OPTIONS] --build-dir DIR Build directory (default: /etc/zfsbootmenu) --output-dir DIR Output directory (default: /build) --ssh Enable Dropbear SSH (port 222) --ssh-port PORT SSH port (default: 222) --network MODE DHCP|STATIC (default: DHCP) --ip-config IP::GW:::IF Manual ip= param --build-now Auto-run ./zbm-builder.sh after setup --help, -h Show help EOF } # Validate prerequisites FIRST for cmd in curl dropbearkey awk ip; do command -v "$cmd" >/dev/null 2>&1 || { echo "ERROR: $cmd is required but not installed." >&2 echo "Install: sudo pacman -S curl dropbear inetutils" >&2 exit 1 } done # Defaults BUILD_DIR="/etc/zfsbootmenu" OUTPUT_DIR="" SSH_ENABLED=0 SSH_PORT=222 NETWORK_MODE="DHCP" IP_CONFIG_OVERRIDE="" AUTO_BUILD=0 # Parse arguments (strict - no positional args) while [[$# -gt 0]]; do case "$1" in --help | -h) show_help exit 0 ;; --build-dir) BUILD_DIR="$2" shift 2 ;; --output-dir) OUTPUT_DIR="$2" shift 2 ;; --ssh) SSH_ENABLED=1 shift ;; --ssh-port) [[ "$2" =~ ^[0-9]+$ && $2 -ge 1 && $2 -le 65535 ]] || { echo "ERROR: Invalid port" exit 1 } SSH_PORT="$2" shift 2 ;; --network) NETWORK_MODE="${2:-DHCP}" shift 2 ;; --ip-config) IP_CONFIG_OVERRIDE="$2" shift 2 ;; --build-now) AUTO_BUILD=1 shift ;; -\*) echo "ERROR: Unknown option '$1'" >&2 show_help >&2 exit 1 ;; esac done ZFSBOOTMENU_VERSION="$(lastversion --format tag zbm-dev/zfsbootmenu)" OUTPUT_DIR="${OUTPUT_DIR:-$BUILD_DIR/build}" ZBM_BUILDER_LATEST_URL="https://raw.githubusercontent.com/zbm-dev/zfsbootmenu/refs/tags/$ZFSBOOTMENU_VERSION/zbm-builder.sh" ZBM_BUILDER_MASTER_URL="https://raw.githubusercontent.com/zbm-dev/zfsbootmenu/refs/heads/master/zbm-builder.sh" ZBM_GIT="https://github.com/zbm-dev/zfsbootmenu" ZBM_GIT_HOME="/var/tmp/zfsbootmenu" [[$EUID -eq 0]] || { echo "ERROR: Must run as root (sudo)" exit 1 } echo "ZFSBootMenu UKI Builder (v3.1.x) - Enterprise Production" echo " Build: $BUILD_DIR" echo " Output: $OUTPUT_DIR" echo " SSH: $([[$SSH_ENABLED == 1]] && echo "port $SSH_PORT" || echo "disabled")" # Download the git repo if [ ! -d "${ZBM_GIT_HOME}" ]; then echo "Cloning repository into ${ZBM_GIT_HOME}" git clone --depth=1 --branch "${ZFSBOOTMENU_VERSION}" --single-branch "${ZBM_GIT}" "${ZBM_GIT_HOME}" else echo "Repository already exists at ${ZBM_GIT_HOME}. Updating..." cd "${ZBM_GIT_HOME}" || exit # Fetch latest changes without pulling git fetch origin "${ZFSBOOTMENU_VERSION}" # Check if current branch is up to date if ! git rev-parse --short HEAD | grep -q "$(git rev-parse --short origin/"${ZFSBOOTMENU_VERSION}")"; then echo "Updating to latest commit of ${ZFSBOOTMENU_VERSION}" git checkout "${ZFSBOOTMENU_VERSION}" git reset --hard "origin/${ZFSBOOTMENU_VERSION}" else echo "Already up to date." fi fi # Create directories # mkdir -p "$BUILD_DIR"/{hooks/{early,late},rc.d,cleanup.d,mkinitcpio.d,etc/dropbear} "$OUTPUT_DIR" rsync -a --include='_/' --exclude='_' ${ZBM_GIT_HOME}/etc "${BUILD_DIR}" mkdir -p "$OUTPUT_DIR" cd "$BUILD_DIR" # Download/update builder (idempotent) # if [[! -f zbm-builder.sh]]; then # curl -fsSL -o zbm-builder.sh "$ZBM_BUILDER_URL" && chmod 755 zbm-builder.sh # elif [[$(curl -s --head "$ZBM_BUILDER_URL" | head -n1) != *"200"*]]; then # echo "WARNING: Cannot verify builder URL - using existing zbm-builder.sh" # fi # Copy the builder from the git repo location if [[! -f zbm-builder.sh]]; then if [[-f "${ZBM_GIT_HOME}/zbm-builder.sh"]]; then cp "${ZBM_GIT_HOME}/zbm-builder.sh" "${BUILD_DIR}" else cat <<-MESSAGE WARNING: zbm-builder.sh not found. Did your git update fail or is ${ZBM_GIT_HOME} unavailable? Exiting! MESSAGE exit 1 fi else echo "✓ zbm-builder.sh included." fi # Auto-detect network params for SSH NETWORK*PARAMS="" if [[$SSH_ENABLED == 1]]; then if [[-n "$IP_CONFIG_OVERRIDE"]]; then NETWORK_PARAMS="$IP_CONFIG_OVERRIDE" echo " Network: Manual ($NETWORK_PARAMS)" elif [["$NETWORK_MODE" == "STATIC"]]; then INTERFACE=$(ip route show default | awk '/default/ {print $5; exit}') [[ -n "$INTERFACE" ]] || { echo "WARNING: No default route for STATIC mode"; } IP_CIDR=$(ip -4 addr show "$INTERFACE" 2>/dev/null | awk '/inet / {print $2; exit}') [[ -n "$IP_CIDR" ]] || { echo "WARNING: No IPv4 address on $INTERFACE"; } IP_ADDR="${IP_CIDR%%/*}" PREFIX="${IP_CIDR#_/}" GATEWAY=$(ip route show default | awk '/default/ {print $3; exit}') [[ -n "$IP_ADDR" && -n "$GATEWAY" ]] && NETWORK_PARAMS="ip=$IP_ADDR::$GATEWAY:$PREFIX::$INTERFACE:none" fi NETWORK_PARAMS="${NETWORK_PARAMS:-ip=dhcp}" echo " Network: $NETWORK_PARAMS" fi # Construct the kernel boot commandline ZBM_RNG_PARAM="" if [[$SSH_ENABLED == 1]]; then ZBM_RNG_PARAM="rng_core.default_quality=1000" fi FULL_CMDLINE="loglevel=4 zbm.import_delay=10 $ZBM_SSH_PARAM $ZBM_RNG_PARAM $NETWORK_PARAMS" echo "DEBUG: Full Kernel CommandLine: $FULL_CMDLINE" # Clean YAML generation ZBM_SSH_PARAM=$([[$SSH_ENABLED == 1]] && echo "zbm.ssh=1" || echo "") cat >config.yaml </dev/null || true # Builder config cat >zbm-builder.conf <mkinitcpio.d/zbm.conf <rc.d/10-grub-compat <<'EOF' #!/bin/sh echo "FONT=ter-v14b" >> /etc/vconsole.conf echo "KEYMAP=uk" >> /etc/vconsole.conf EOF chmod 755 rc.d/10-grub-compat cat >rc.d/consolefont </dev/null 2>&1 done # FAIL-SAFE user key handling (enterprise grade) if [[-n "${SUDO_USER:-}"]]; then REAL_USER_HOME=$(getent passwd "$SUDO_USER" | cut -d: -f6) [[-n "$REAL_USER_HOME" && -d "$REAL_USER_HOME"]] || { echo "ERROR: Cannot find home directory for sudo user '$SUDO_USER'" >&2 exit 1 } else REAL_USER_HOME="/root" fi USER_KEYS="$REAL_USER_HOME/.ssh/authorized_keys" if [[! -f "$USER_KEYS" || ! -s "$USER_KEYS"]]; then echo "ERROR: No authorized_keys found at $USER_KEYS" >&2 echo " Generate: ssh-keygen -t ed25519 -f ~/.ssh/id_zbm" >&2 echo " Add pubkey: cat ~/.ssh/id_zbm.pub >> ~/.ssh/authorized_keys" >&2 exit 1 fi VALID_KEYS=$(grep -cE '^(ssh-rsa|ssh-ed25519|ecdsa-sha2-nistp256|sk-ssh-ed25519)' "$USER_KEYS") [[$VALID_KEYS -gt 0]] || { echo "ERROR: No valid public keys found in $USER_KEYS" >&2 exit 1 } cp -f "$USER_KEYS" etc/dropbear/authorized_keys chmod 600 etc/dropbear/authorized_keys echo "✓ Imported $VALID_KEYS valid SSH public key(s)" # Enterprise Dropbear startup (-R = no DNS delay) cat >rc.d/20-ssh.conf </dev/null || true install -m 600 /etc/dropbear/authorized_keys /root/.ssh/authorized_keys 2>/dev/null || true install -m 600 /etc/dropbear/authorized_keys /etc/dropbear/authorized_keys 2>/dev/null || true exec /usr/sbin/dropbear -F -E -R -p $SSH_PORT EOF chmod 755 rc.d/20-ssh.conf fi # Cleanup hook cat >cleanup.d/99-cleanup <<'EOF' #!/bin/sh rm -f hostid zpool.cache EOF chmod 755 cleanup.d/99-cleanup # Auto-build if requested if [[$AUTO_BUILD == 1]]; then echo "→ Executing zbm-builder.sh..." ./zbm-builder.sh || { echo "ERROR: zbm-builder.sh failed!" exit 1 } fi cat < menu (import pools) 5. Kexec → your OS kernel DEPLOYMENT: To get the same behaviour as the UKI EFI boot the GRUB_DEFAULT commandline must match: ${FULL_CMDLINE} sudo deploy-zbm.sh --force will call: cd $BUILD_DIR && sudo ./zbm-builder.sh and unpack the created UKI EFI file into /boot/zfsbootmenu SECURE BOOT (if enabled): sbsign --key /path/db.key --cert /path/db.crt $OUTPUT_DIR/\*.EFI RECOVERY ACCESS: ssh root@ -p $SSH_PORT zfsbootmenu> [navigate menu] EOF