Failing shell code under busybox 1.36.1 that worked with 1.31.1
David Leonard
d+busybox at adaptive-enterprises.com
Thu Feb 15 08:45:44 UTC 2024
> _result="$_result '${_p//'/'\"'\"'}'"
Busybox ash has a CONFIG_ASH_BASH_COMPAT config to get this ${var//...}
replacement behaviour. And, in bash, the ' in the pattern section is treated
as a quote start. To give the ' a literal meaning, use \'
eg ${_p//\'/..repl..}
In current busybox ash you will run into a bug where quotes aren't
handled in the pattern, and even loses the rest of the word, as you
observed. That is, given this script:
var="don't g/o"
echo "${var/'/'o}!"
echo "${var/\'/\'o}!"
bash outputs:
don't g! (pattern="/o" repl="")
don'ot g/o! (pattern="'", repl="'o")
and busybox ash outputs:
don't g/o (pattern=?, repl=?, NOTE: lost !)
don'ot g/o! (pattern="'", repl="'o")
Here's a patch to make ash a little bit closer to bash.
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -7058,6 +7058,7 @@ subevalvar(char *start, char *str, int strloc,
*/
repl = NULL;
if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
+ int quoted = 0;
/* Find '/' and replace with NUL */
repl = start;
/* The pattern can't be empty.
@@ -7073,6 +7074,11 @@ subevalvar(char *start, char *str, int strloc,
repl = NULL;
break;
}
+ /* Handle quoted patterns */
+ if ((unsigned char)*repl == CTLQUOTEMARK)
+ quoted ^= 1;
+ if (quoted)
+ goto literal;
if (*repl == '/') {
*repl = '\0';
break;
@@ -7084,6 +7090,7 @@ subevalvar(char *start, char *str, int strloc,
/* Handle escaped slashes, e.g. "${v/\//_}" (they are CTLESC'ed by this point) */
if ((unsigned char)*repl == CTLESC && repl[1])
repl++;
+ literal:
repl++;
}
}
new file mode 100644
index 000000000..23aecb5ae
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_bash_repl_quoted.right
@@ -0,0 +1,3 @@
+ab.
+axb.
+x/b.
new file mode 100644
index 000000000..d2ba39578
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_bash_repl_quoted.tests
@@ -0,0 +1,4 @@
+v=a/b
+echo "${v/'/'}."
+echo "${v/'/'/x}."
+echo "${v/'a'/x}."
On Wed, 14 Feb 2024, Harvey wrote:
> Sorry, posted the wrong version of the pack_args() funtion:
> I have payed with possible solutions for so long that I forgot the
> restore the original before posting -blush-
>
> Here is the original version:
> ------------------------------------------------------------------------
>
> # Packs arguments into a single string.
> #
> # Input:
> # $1 = name of variable receiving the arguments in such a way that
> # (re)evaluating them provides the original arguments
> # $2... = arguments
> # Example:
> # func() {
> # local args
> # pack_args args "$@"
> # send_rpc func_impl "$args"
> # }
> # # (in another process) > # receive_rpc() {
> # local func="$1"
> # local args="$2"
> # eval $func "$args"
> # }
> pack_args()
> {
> local _p _resvar=$1 _result= _value
> shift
> for _p
> do
> _result="$_result '${_p//'/'\"'\"'}'"
> done
> eval $_resvar=\${_result\# }
> }
> ------------------------------------------------------------------------
>
> Harvey
>
> Am 14.02.24 um 19:06 schrieb Harvey:
>> Hello,
>>
>> I am part of the team of a small router distribution called fli4l:
>> https://www.fli4l.de/doku.php?id=start.
>>
>> We use buildroot to generate our images. Due to a hardware crash and the
>> leaving of our main developer we are facing the dead of our project. But
>> there are still some people left and at least we want to try to keep it
>> alive 😉
>>
>> That said - the first we have to try is the update of our buildroot
>> base. A lot of work is already done and but we struggle with the busybox
>> update, especially with the ash shell contained. When updating to
>> Version 1.36.1 we are facing script failures in places that did work
>> until busybox 1.31.1.
>>
>> I have tried to debug the code and it seems that the error is contained
>> in a helper include for string handling.
>>
>> The code is:
>> ------------------------------------------------------------------------
>> # Packs arguments into a single string.
>> #
>> # Input:
>> # $1 = name of variable receiving the arguments in such a way that
>> # (re)evaluating them provides the original arguments
>> # $2... = arguments
>> # Example:
>> # func() {
>> # local args
>> # pack_args args "$@"
>> # send_rpc func_impl "$args"
>> # }
>> # # (in another process)
>> # receive_rpc() {
>> # local func="$1"
>> # local args="$2"
>> # eval $func "$args"
>> # }
>> pack_args()
>> {
>> local _p _resvar=$1 _result= _value
>> shift
>> for _p
>> do
>> _result="$_result '${_p//'/'\"'\"'}''"
>> done
>> eval $_resvar=\${_result\# }
>> }
>> ------------------------------------------------------------------------
>>
>> The debug trace (using set -x) in V1.31.1 looks like this:
>>
>> + pack_args exit_trap_0 sync_unlock_all_resources
>> + local _p '_resvar=exit_trap_0' '_result=' _value
>> + shift
>> + _result=' '"'"'sync_unlock_all_resources'"'"
>> + eval 'exit_trap_0=${_result#' '}'
>> + exit_trap_0=''"'"'sync_unlock_all_resources'"'"
>> + exit_trap_num=1
>> + return 0
>>
>> ------------------------------------------------------------------------
>> while in V1.36.1 it looks like this:
>>
>> + pack_args exit_trap_0 sync_unlock_all_resources
>> + local _p '_resvar=exit_trap_0' '_result=' _value
>> + shift
>> + _result=' '"'"'sync_unlock_all_resources'
>> ^^^^ missing quote
>> + eval 'exit_trap_0=${_result#' '}'
>> + exit_trap_0=''"'"'sync_unlock_all_resources'
>> ^^^^ missing quote
>> + exit_trap_num=1
>> + return 0
>>
>> The eval line then fails with 'Unterminated quoted string'
>>
>> Unfortunately I can't tell where the difference lies.
>>
>> I hope someone can help or at least point me in the right direction.
>>
>> Greetings
>> Harvey
>> _______________________________________________
>> busybox mailing list
>> busybox at busybox.net
>> http://lists.busybox.net/mailman/listinfo/busybox
> _______________________________________________
> busybox mailing list
> busybox at busybox.net
> http://lists.busybox.net/mailman/listinfo/busybox
>
More information about the busybox
mailing list