fix: store and execute multiline commands in pos bank
gates / consistency-and-conventions (push) Successful in 20s

bank.env is line-oriented (name|description|command) so commands with
real newlines spanned records: bank_load truncated the command to its
first line and the remaining script lines became bogus entries. The
bank_get+cut -f3 retrieval path also truncated at embedded tabs.

- lib/bank-lib.sh: v2 format escapes backslash->\\ and newline->\\n
  in the command field, writes # BANK_VERSION: 2; bank_load decodes
  with printf %b only for v2 files, so existing v1 files load raw
  (backward compatible, verified against the real ts-google entry).
- bin/pos-bank: cmd_show/cmd_run/cmd_edit now read fields from the
  BANK_* arrays via bank_find instead of bank_get+cut.
- tests/t-bank.sh: +13 checks (71 total) - multiline round-trip exact
  bytes, literal backslash-n, v1 raw-backslash compat, v1+re-save
  byte-identical, CLI show/run full script.
This commit is contained in:
Your Name
2026-09-12 08:25:40 -04:00
parent e23d57e551
commit 11b4a679a8
5 changed files with 132 additions and 22 deletions
+12 -4
View File
@@ -4,6 +4,8 @@
#
# Storage: ~/.config/linux_post_install/bank.env
# Format: name|description|command
# v2: commands with real newlines are saved with \\ (backslash) and \n
# (newline) escapes; files written by v1 have no marker and load raw.
#
# Contracts:
# * Defines ONLY bank_* functions — sourcing never clobbers a tool's helpers.
@@ -20,12 +22,13 @@ bank_load() {
BANK_NAMES=(); BANK_DESCS=(); BANK_CMDS=()
[ -f "$BANK_FILE" ] || return 0
local line name desc cmd
local line name desc cmd v2=0
while IFS= read -r line; do
[[ "$line" =~ ^[[:space:]]*# ]] && continue
[[ "$line" =~ ^[[:space:]]*# ]] && { [[ "$line" == *"BANK_VERSION: 2"* ]] && v2=1; continue; }
[[ -z "${line// /}" ]] && continue
IFS='|' read -r name desc cmd <<< "$line"
[[ -z "$name" ]] && continue
[ "$v2" -eq 1 ] && cmd="$(printf '%b' "$cmd")"
BANK_NAMES+=("$name")
BANK_DESCS+=("${desc:-}")
BANK_CMDS+=("${cmd:-}")
@@ -42,10 +45,15 @@ bank_save() {
tmp="$(mktemp "${dir}/.bank.XXXXXX")"
{
printf '%s\n' "# Command Bank — managed by pos bank (do not hand-edit)"
printf '%s\n' "# Format: name|description|command"
printf '%s\n' "# Format: name|description|command (\\n = escaped newline in command)"
printf '%s\n' "# BANK_VERSION: 2"
local i
for ((i = 0; i < ${#BANK_NAMES[@]}; i++)); do
printf '%s|%s|%s\n' "${BANK_NAMES[$i]}" "${BANK_DESCS[$i]}" "${BANK_CMDS[$i]}"
local cmd="${BANK_CMDS[$i]}"
# Escape: every backslash → \\, every real newline → \n (keeps one record per physical line)
cmd="${cmd//\\/\\\\}"
cmd="${cmd//$'\n'/\\n}"
printf '%s|%s|%s\n' "${BANK_NAMES[$i]}" "${BANK_DESCS[$i]}" "$cmd"
done
} > "$tmp"
chmod 600 "$tmp"