Re: setarray[unsetkey] doesn't trigger nounset in arithmetic context

2023-05-21 Thread Martin D Kealey
(I assume this is a continuation of the discussion in #bash on Libera.chat
yesterday?)

The primary use of `set -u` is to detect misspelled variable names, and
misspelled keys for associative arrays seems like a reasonable extension of
that, if you assume that they're in some sense a fixed list, like the fixed
fields in a C struct.

However, Bash's associative arrays more closely resemble Perl hashes, PHP
arrays, Python dicts, JS objects, etc, where "missing" entities are normal
and expectable. At least some - maybe most - of the time they fulfill roles
more similar to these open dynamic structures than to traditional fixed
structs, and treating "unset" as a fatal error is not the experience that
programmers have using other scripting languages, and is therefore, I would
argue, not something that should be added to the Shell, and certainly not
as a quiet change to the existing `set -u`.

This leads to the conclusion that the fail-if-unset behaviour should be
specified on a per-array basis, which means it should be part of `declare`
and `local` rather than a global through `set` or `shopt`.

If we're heading down this route I have a bunch of other things that
overdue to be fixed, including:

1. ${array[@]:start:count} can under some circumstances return elements
with indeces >= start+count, completely contravening expectations from
other programming languages. There should be a `declare` or `local` option
to fix that, and/or a shopt setting.
2. ${#array[@]} gives the number of elements rather than the last index
(minus 1). Being able to declare an array as non-sparse (so that all gaps
"exist" with some default value) would fix this.

-Martin


Re: setarray[unsetkey] doesn't trigger nounset in arithmetic context

2023-05-21 Thread Greg Wooledge
On Mon, May 22, 2023 at 07:01:18AM +1000, Martin D Kealey wrote:
> [...] and treating "unset" as a fatal error is not the experience that
> programmers have using other scripting languages, and is therefore, I would
> argue, not something that should be added to the Shell, and certainly not
> as a quiet change to the existing `set -u`.

Just for the record, Tcl's unset command generates an error if you use
it on a nonexistent variable (which includes a nonexistent array
element).  But it also offers a -nocomplain option, which you may use
if you want to unset something that may or may not currently exist.

(End tangent.)



Re: setarray[unsetkey] doesn't trigger nounset in arithmetic context

2023-05-21 Thread Martin D Kealey
I just realised I expressed an opinion about associative arrays while the
original post was about indexed arrays.

My take on this is that indexed arrays are almost always "dynamic"; while
it's possible to use constant indices to emulate a struct, that is unusual.
For most purposes the flexibility in adding & removing from an array is a
feature, not a bug.

The support for missing elements or 'empty slots' is a bit unusual, but
once that's given, it's entirely predictable that "missing" elements are
just a normal kind of data, which is where I was heading with my suggestion
about non-sparse arrays.

On Mon, 22 May 2023 at 07:01, Martin D Kealey 
wrote:
[...]

> 2. ${#array[@]} gives the number of elements rather than the last index
> (minus 1). Being able to declare an array as non-sparse (so that all gaps
> "exist" with some default value) would fix this.
>

-Martin


Re: setarray[unsetkey] doesn't trigger nounset in arithmetic context

2023-05-21 Thread Emanuele Torre
On Mon, May 22, 2023 at 09:27:17AM +1000, Martin D Kealey wrote:
> I just realised I expressed an opinion about associative arrays while the
> original post was about indexed arrays.
> 

I simply reported that, specifically in arithmetic contexts, when using
a variable (not as the lhs of an = assignment), bash only checks that
the variable name before "[" is bound to decide whether to trigger
nounset or not:

  $ set -u
  $ echo "$(( a[0] ))"   # correct, a is not set
  bash: a: unbound variable
  $ declare b
  $ echo "$(( b[0] ))"   # correct, b is declared, but not set
  bash: b: unbound variable
  $ c=()
  $ echo "$(( c[0] ))"   # wrong, it is supposed to error, c[0] is not set
  0
  $ echo "$(( c[1] ))"   # wrong, it is supposed to error, c[1] is not set
  0
  $ d=11
  $ echo "$(( d[0] ))"   # correct, d[0] is set
  11
  $ echo "$(( d[1] ))"   # wrong, it is supposed to error, d[1] is not set
  0
  $ e=([1]= )
  $ echo "$(( e[1] ))"   # correct, e[1] is set
  0

If the variable has a subscript, it is supposed to also check that the
array slot is set, and trigger nounset if it is not.

If you run the same commands I marked as wrong above with regular
non-unset-immune parameter expansions instead of arithmetic expansions,
you will see that nounset is triggered if z is set, but the specific
index is not.

  $ set -u
  $ unset -v z
  $ echo "${z[0]}"
  bash: z[0]: unbound variable
  $ echo "${z[10*RANDOM+1]}"
  bash: z[10*RANDOM+1]: unbound variable
  $ z=() # even if z is set, it still trigger nounset
  $ echo "${z[0]}"
  bash: z[0]: unbound variable
  $ echo "${z[10*RANDOM+1]}"
  bash: z[10*RANDOM+1]: unbound variable
  $ d=11
  $ echo "${d[1]}"
  bash: d[1]: unbound variable

I think this behaviour is unintented and only happens because the code I
linked in the original post is forgetting to check that the array slot
is set after expanding variables with a subscript, so it doesn't issue
the error.

This has nothing to do specifically with associative arrays, though they
are also affected by the problem. And it also has nothing to do with [*]
and [@] having special treatment when used with nounset to follow the
behaviour of $* and $@ in POSIX sh.

I am not really proposing a major change to the behaviour of nounset, I
am only pointing out the incorrect bash behaviour for that case.

 emanuele6



Re: `wait -n` returns 127 when it shouldn't

2023-05-21 Thread Aleksey Covacevice
On Thu, May 18, 2023 at 3:07 PM Chet Ramey  wrote:
>
> On 5/18/23 7:51 AM, Robert Elz wrote:
>
> > Apparently, in bash, if the code is running in a (shell) loop (like inside
> > a while, or similar, loop) then each iteration around the loop, any jobs 
> > that
> > have exited, but not been cleaned already, are removed from the queue (the
> > jobs table in practice, though bash may also have something else).
> >
> > That's really broken, and should be fixed (but has apparently been that
> > way for decades, and no-one noticed).
>
> This isn't a problem, and is a red herring. The code that manages that list
> makes sure to keep as many jobs in the list as POSIX requires, subject to
> the maxchild resource limit.
>
>
> > In the script in question, the offending loop isn't the one in the main
> > program - in that for each iteration the background processes are started,
> > and waited for, in each iteration, but the one in the waitjobs function.
> > which (appears at first glance, which is all the analysis shells ever do)
> > to be an infinite loop, so each time around, if there are any completed
> > jobs in the table, they're removed.
>
> No, this isn't what happens. The problem is that the shell reaps both jobs,
> but the `wait -n' code had a race condition that prevented it from finding
> a job in the list.
>

I fail to see where the race condition in `true & wait -n` is. Whether the
'underlying function' has a race condition is the true red herring here.

Also, the manual states:

'If the -n option is supplied, wait waits for a single job from the
list of ids or,
if no ids are supplied, any job, to complete and returns its exit status.'

`true & wait -n` returning 127 means `wait -n` did not wait for 'any job';
in fact, it waited for no job. The subsequent part about
'unwaited-for' children is
either irrelevant or contradictory to the above given the current scenario.

> --
> ``The lyf so short, the craft so long to lerne.'' - Chaucer
>  ``Ars longa, vita brevis'' - Hippocrates
> Chet Ramey, UTech, CWRUc...@case.eduhttp://tiswww.cwru.edu/~chet/
>