From a3a6db9ad40a71dbc92b644c220b849d225ef4a8 Mon Sep 17 00:00:00 2001 From: Isaac Freeman Date: Tue, 1 Sep 2026 02:38:21 -0400 Subject: [PATCH] Support callers with nounset enabled --- bj-1line.sh | 2 +- bj-80-col.sh | 6 +++--- bj-90-col.sh | 18 +++++++++--------- bj.sh | 2 ++ test.sh | 34 ++++++++++++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 13 deletions(-) diff --git a/bj-1line.sh b/bj-1line.sh index acdc5e7..169b8e6 100755 --- a/bj-1line.sh +++ b/bj-1line.sh @@ -1,2 +1,2 @@ # (C) Isaac Freeman (memotype@gmail.com). See https://github.com/memotype/bj.sh -bj()([[ $1 = - ]]||exec<<<"$1";shift;c=;r(){ l=$c;IFS= read -rN1 c;};p(){ printf %s "${o[@]}";};s(){ p=$1;[[ $p ]]||o=();while r;do [[ $p&&! $q ]]&&o+=("$l");case $c in \\)r||break;[[ $p&&$q ]]||o+=("$l${c::!p}");;\")break;;*)[[ $p ]]||o+=("$c");;esac;done;};co(){ n= b=1;while r;do [[ $q ]]||o+=("$l");case $c in \")[[ $1||! $q ]]&&s 1||{ s;k=$(p);};;[|{)((b++));;]|\})((--b))||{ [[ $q ]]&&exit 1;o+=("$c");break;};;:)[[ $1$b = 1&&$q&&$k = "$q" ]]&&break;;,)((0$1&&b==1))&&[[ $q = $((++n)) ]]&&break;;esac;done;};for q in "$@" "";do shift&&[[ ! $q ]]&&return 2;x=0;o=();while r;do case $q$c in *[[:space:]])! :;;\")s;;[tfn0-9-])while o+=("$c")&&r&&[[ $c =~ [-+.0-9Ea-z] ]];do :;done;;*{)co;;*[)[[ $q = 0 ]]||co 1;;?)return 2;;*)return 1;;esac&&x=1&&break;done;done;p;((x));) +bj()(set +u;[[ $1 = - ]]||exec<<<"$1";shift;c=;r(){ l=$c;IFS= read -rN1 c;};p(){ printf %s "${o[@]}";};s(){ p=$1;[[ $p ]]||o=();while r;do [[ $p&&! $q ]]&&o+=("$l");case $c in \\)r||break;[[ $p&&$q ]]||o+=("$l${c::!p}");;\")break;;*)[[ $p ]]||o+=("$c");;esac;done;};co(){ n= b=1;while r;do [[ $q ]]||o+=("$l");case $c in \")[[ $1||! $q ]]&&s 1||{ s;k=$(p);};;[|{)((b++));;]|\})((--b))||{ [[ $q ]]&&exit 1;o+=("$c");break;};;:)[[ $1$b = 1&&$q&&$k = "$q" ]]&&break;;,)((0$1&&b==1))&&[[ $q = $((++n)) ]]&&break;;esac;done;};for q in "$@" "";do shift&&[[ ! $q ]]&&return 2;x=0;o=();while r;do case $q$c in *[[:space:]])! :;;\")s;;[tfn0-9-])while o+=("$c")&&r&&[[ $c =~ [-+.0-9Ea-z] ]];do :;done;;*{)co;;*[)[[ $q = 0 ]]||co 1;;?)return 2;;*)return 1;;esac&&x=1&&break;done;done;p;((x));) diff --git a/bj-80-col.sh b/bj-80-col.sh index 0dea8ae..29f29c1 100755 --- a/bj-80-col.sh +++ b/bj-80-col.sh @@ -1,7 +1,7 @@ # (C) Isaac Freeman (memotype@gmail.com). See https://github.com/memotype/bj.sh -bj()([[ $1 = - ]]||exec<<<"$1";shift;c=;r(){ l=$c;IFS= read -rN1 c;};p(){ \ -printf %s "${o[@]}";};s(){ p=$1;[[ $p ]]||o=();while r;do [[ $p&&! $q ]]&&o+=(\ -"$l");case $c in \\)r||break;[[ $p&&$q ]]||o+=("$l${c::!p}");;\")break;;*)[[ \ +bj()(set +u;[[ $1 = - ]]||exec<<<"$1";shift;c=;r(){ l=$c;IFS= read -rN1 c;};p()\ +{ printf %s "${o[@]}";};s(){ p=$1;[[ $p ]]||o=();while r;do [[ $p&&! $q ]]&&o+=\ +("$l");case $c in \\)r||break;[[ $p&&$q ]]||o+=("$l${c::!p}");;\")break;;*)[[ \ $p ]]||o+=("$c");;esac;done;};co(){ n= b=1;while r;do [[ $q ]]||o+=("$l");case \ $c in \")[[ $1||! $q ]]&&s 1||{ s;k=$(p);};;[|{)((b++));;]|\})((--b))||{ [[ $q \ ]]&&exit 1;o+=("$c");break;};;:)[[ $1$b = 1&&$q&&$k = "$q" ]]&&break;;,)((0$1&& diff --git a/bj-90-col.sh b/bj-90-col.sh index a587db9..66e8694 100755 --- a/bj-90-col.sh +++ b/bj-90-col.sh @@ -1,10 +1,10 @@ # (C) Isaac Freeman (memotype@gmail.com). See https://github.com/memotype/bj.sh -bj()([[ $1 = - ]]||exec<<<"$1";shift;c=;r(){ l=$c;IFS= read -rN1 c;};p(){ printf %s \ -"${o[@]}";};s(){ p=$1;[[ $p ]]||o=();while r;do [[ $p&&! $q ]]&&o+=("$l");case $c in \\)r\ -||break;[[ $p&&$q ]]||o+=("$l${c::!p}");;\")break;;*)[[ $p ]]||o+=("$c");;esac;done;};co\ -(){ n= b=1;while r;do [[ $q ]]||o+=("$l");case $c in \")[[ $1||! $q ]]&&s 1||{ s;k=$(p);} -;;[|{)((b++));;]|\})((--b))||{ [[ $q ]]&&exit 1;o+=("$c");break;};;:)[[ $1$b = 1&&$q&&$k \ -= "$q" ]]&&break;;,)((0$1&&b==1))&&[[ $q = $((++n)) ]]&&break;;esac;done;};for q in "$@" \ -"";do shift&&[[ ! $q ]]&&return 2;x=0;o=();while r;do case $q$c in *[[:space:]])! :;;\")s -;;[tfn0-9-])while o+=("$c")&&r&&[[ $c =~ [-+.0-9Ea-z] ]];do :;done;;*{)co;;*[)[[ $q = 0 \ -]]||co 1;;?)return 2;;*)return 1;;esac&&x=1&&break;done;done;p;((x));) +bj()(set +u;[[ $1 = - ]]||exec<<<"$1";shift;c=;r(){ l=$c;IFS= read -rN1 c;};p(){ printf \ +%s "${o[@]}";};s(){ p=$1;[[ $p ]]||o=();while r;do [[ $p&&! $q ]]&&o+=("$l");case $c in \ +\\)r||break;[[ $p&&$q ]]||o+=("$l${c::!p}");;\")break;;*)[[ $p ]]||o+=("$c");;esac;done;} +co(){ n= b=1;while r;do [[ $q ]]||o+=("$l");case $c in \")[[ $1||! $q ]]&&s 1||{ s;k=$(p) +};;[|{)((b++));;]|\})((--b))||{ [[ $q ]]&&exit 1;o+=("$c");break;};;:)[[ $1$b = 1&&$q&& +$k = "$q" ]]&&break;;,)((0$1&&b==1))&&[[ $q = $((++n)) ]]&&break;;esac;done;};for q in \ +"$@" "";do shift&&[[ ! $q ]]&&return 2;x=0;o=();while r;do case $q$c in *[[:space:]])! :;; +\")s;;[tfn0-9-])while o+=("$c")&&r&&[[ $c =~ [-+.0-9Ea-z] ]];do :;done;;*{)co;;*[)[[ $q \ += 0 ]]||co 1;;?)return 2;;*)return 1;;esac&&x=1&&break;done;done;p;((x));) diff --git a/bj.sh b/bj.sh index 2be0c7d..fc9e406 100755 --- a/bj.sh +++ b/bj.sh @@ -3,6 +3,8 @@ # Copyright Isaac Freeman (memotype@gmail.com), licensed under the MIT license bj() ( + set +u + # If $1 is -, just read from stdin, otherwise we push $1 to stdin. # The JSON value is read as a stream of characters from stdin. [[ $1 = - ]] || exec <<<"$1" diff --git a/test.sh b/test.sh index b4dbe12..584af18 100755 --- a/test.sh +++ b/test.sh @@ -103,6 +103,40 @@ runstdin '[1,2]' '[1,2]' runstdin '{"a":1}' '{"a":1}' runstdin node-1 '{"metadata":{"name":"node-1"}}' metadata name +# The parser must work when inherited shell options include nounset without +# changing the caller's option state. +echo "*** nounset caller: $src" +bash -u /dev/stdin "$src" <<'EOF' \ + || fail "$src failed with nounset enabled" +src=$1 +. "$src" + +check() { + local expected=$1 + shift + local c r + r=$(bj "$@") + c=$? + [[ $r = "$expected" && $c = 0 ]] || exit 1 +} + +json='{"object":{"string":"value","number":42,"true":true,"false":false,"null":null,"array":[0,{"nested":"hit"}]}}' +check '{"string":"value","number":42,"true":true,"false":false,"null":null,"array":[0,{"nested":"hit"}]}' "$json" object +check value "$json" object string +check 42 "$json" object number +check true "$json" object true +check false "$json" object false +check null "$json" object null +check '[0,{"nested":"hit"}]' "$json" object array +check hit "$json" object array 1 nested + +r=$(bj "$json" object missing) +c=$? +[[ -z $r && $c = 1 ]] || exit 1 +[[ $- = *u* ]] || exit 1 +EOF +echo pass + # Array out of bounds test runteststatus 1 '' '[0, 1, 2, 3]' 4