Ha yes I lost sight of <(jobs -t), I think it is a good improvement to the challenge :)
Regarding the local t, you right, I missed to precise that any function that do output parameters via nameref should namespace all their locals and check the namespace that is indeed a bit combersome, that's why I tend to stay away from that :) yet it was asked.. Here I typed to fast (challenged by providing the shortest answer :) ) and throwed away my basics principles :) Regarding the code injection I am not sure I got it. If you are sitting at a prompt, why would you trick unicorn:~$ njobs_ref 'x[0$(date>&2)]' when you could simply type unicorn:~$ date I assume protected script/source (the ones you can't write into), are wise enough not to run command based on user input, in short I guess no protected script are doing thing like read in; eval $in :) that is the simplest code injection :) and then would never let you have a chance to enter 'x[0$(date>&2)]' at any time. In all case since doing output parameter require some kind of name spacing check it would reject input of the form 'x[0$(date>&2)]' I guess a typical output parameter function should ressemble something like this function foo { [[ ! "$1" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] && echo "Invalid parameter name '$1'" >&2 && return 1 [ "${1:0:4}" = "foo_" ] && echo "Namespace collision '$1' " >&2 && return 1 typeset -n foo_out="$1" foo_out="value" } This one reject the bad 'x[0$(date>&2)]' only accept scalar variable names as output parameter and reject scalar in the foo() namespace. On Fri, Jul 2, 2021 at 2:01 PM Greg Wooledge <g...@wooledge.org> wrote: > On Fri, Jul 02, 2021 at 09:09:34AM +0200, Phi Debian wrote: > > PW$ function njobs > > > { [ "$1" != "n" ] && typeset -n n="$1" > > > typeset -a t ; readarray t <<<"$(jobs)" ;n=${#t[@]} > > > } > > <<<$() is a poor imitation of < <() which is what you really want. > > readarray -t t < <(jobs); n=${#t[@]} > > Combining <<< and $() only gives an approximation of the same result > (the command substitution strips off all trailing newline characters, > and then the here-string syntax appends one newline), and it's less > efficient, because it involves slurping the entire input into memory, > then writing it out to a temporary file, then opening the temporary > file for input and unlinking it, then reading it back in a second time. > > Using < <() avoids the newline alterations and the temporary file. > > (Note: in some sufficiently new versions of bash, there may not be a > temporary file in some cases. But it's still not the best solution, > as it still involves storing and reading the whole output multiple times.) > > > Stepping back a moment, you're using the name reference version of the > "pass an output variable by reference" strategy. This requires bash 4.3, > which is reasonable, and it requires some additional sanity checks which > you did not show. > > What's really interesting to me is that you did a *partial* sanity check, > refusing to create a circular name reference if the user passed "n" > as their output variable name. But you forgot to check for "t", which is > another local variable you're using. Also, in Linda's original example, > the output variable was literally named "n", so choosing that as your > name reference and explicitly disallowing it is a really spiteful choice. > > Finally, you didn't do any sanity checking of the output variable name > (beyond comparing it to one of your two local variable names), so your > function is susceptible to the same code injection attacks we discussed > earlier in the thread. > > unicorn:~$ njobs_ref() { typeset -n n="$1"; n=42; } > unicorn:~$ njobs_ref 'x[0$(date>&2)]' > Fri Jul 2 07:54:49 EDT 2021 > > As I mentioned a few days ago, all variants of the "pass a variable name > by reference" method are basically equivalent to each other, and all > of them need input sanity checking in order to avoid code injections. > (Some of the variants avoid *some* flavors of code injection, but none of > them avoid this one.) > >