help-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Help-bash] Awkward behavior of empty arrays


From: Andy Chu
Subject: Re: [Help-bash] Awkward behavior of empty arrays
Date: Thu, 31 Aug 2017 02:51:58 -0700

The problems you point out are indeed subtle, but if you follow a few
rules, you can avoid them:

1. Never use anything that isn't in parentheses on the RHS of an array
initialization.

OK:
declare -a A=()
decare -a A=(1 2 3)

INVALID:
declare -a A=  (your example 1)
declare -a A=''
declare -a A='x'

The way you should read this is "I'm assigning a string to an array".  Bash
does the non-obvious thing of coercing the string to an array -- by
creating an array with ONE element.

2. Never reference an array with anything but "address@hidden" or "${A[i]}" 
where
i is an integer (double quotes are mandatory in both cases).

Examples 2 and 3 break this rule -- address@hidden doesn't have double quotes.

also invalid:
"${A}"  # this is an implicit "address@hidden" but is confusing
"${A[*]}"
${A[*]}
address@hidden

3. Don't do anything with arrays except COPY them and SPLICE them into
commands.

See my post for the correct ways to do these two things: "Thirteen
Incorrect Ways and Two Awkward Ways to Use Arrays"

http://www.oilshell.org/blog/2016/11/06.html

In particular, don't expect to compare arrays with [[.

$ declare -a A=(A B C D)
$ declare -a B=('A B' 'C D')

$ echo "address@hidden"
4
$ echo "address@hidden"
2

# arrays compare equal because they're coerced to strings before comparison
$ [[ "address@hidden" == "address@hidden" ]]; echo $?
0

I would say that bash also only has "half an array type".  Bash will coerce
arrays to strings, and strings to arrays, seemingly at random.  It tries
its best to do conversions for you, which is not how any other programming
language with arrays works.  (Although this behavior may have originated
with ksh.)

In fact I was thinking of creating a style guide or mode for my
bash-compatible shell OSH to enforce these rules.

The way you would join or split arrays would be:

declare -a myarray=(a b c)
address@hidden  # join array into string, no quotes

declare mystr='a b c'
declare -a myarray=( $mystr )  # split mystr into myarray, no quotes

ANY OTHER usage where bash does an implicit conversion would be a fatal
runtime error (if this mode is set).  For example, [[ "address@hidden" ==
"address@hidden" ]] would be a fatal runtime error.

You could call this subset of bash the "strict array style" or "hygienic
array style", which I believe is what you're asking for.  It's a set of
rules that let you use arrays in the natural manner, with strict errors.

Andy



On Thu, Aug 31, 2017 at 1:38 AM, Cristian Zoicas <address@hidden>
wrote:

>
> Hello all
>
> I want to create arrays and use them (easily). I am especially
> interested in empty arrays and I felt very uncomfortable with them
> since I found some  counterintuitive or undocumented behaviors.  I
> provide some examples below.
>
> Example 1:
>
>     1 set -u;
>     2 unset A;
>     3 declare -a A=;
>     4 echo "s1";
>     5 echo "size: address@hidden";
>     6 echo "s2";
>     7 for e in address@hidden; do echo "e: ${e}"; done
>
>     Starting with  the line 3  I want to create  an empty array,  so I
>     declare A to have the array  attribute and then I (hope to) assign
>     null to it.  Some  people may say that I am  not right because the
>     manual says  "An array variable  is considered set if  a subscript
>     has been assigned  a value." They are somehow right  because I did
>     not assign any value to any  subscript, so the array should not be
>     initialized. However let's see what is going on.
>
>     Even if I  did not assign any  subscript a value, the  line 5 says
>     that the size  of the array is  1 and it is  possible to reference
>     element ${A[0]} without  receiving any error due to  "set -u". Also
>     address@hidden expands to nothing.
>
>     If the manual  is right, then the array should  not be defined. It
>     should not exist and I should  receive errors due to 'set -u', but
>     even  'declare -p  A' shows  that the  array exists  and it  has a
>     single element whose value is null.
>
>     Is this behavior of assigning null to an array an to have an array
>     with a single null element the desired behavior?
>
> Example 2:
>
>    1 set -u;
>    2 unset A;
>    3 declare -a A=();
>    4 echo "s1";
>    5 echo "size: address@hidden";
>    6 echo "s2";
>    7 for e in address@hidden; do echo "e: $e"; done
>
>    Starting with  the line 3  I want to create  an empty array,  so I
>    declare A to have the array  attribute and then I (hope to) assign
>    an empty array to it.
>
>    The line 5 behaves  well and says that the size of  the array is 0,
>    thus I imagine that the array  is empty. So according to the manual
>    address@hidden should expand to  nothing and  the for loop  in the  line 7
>    should not print anything. Instead the message 'bash: address@hidden: 
> unbound
>    variable' is printed. As if A is not set.
>
>    There is nothing in the manual that says that the () cannot be used
>    to create empty arrays. The syntax is also accepted
>
>    It means that  if the size of the  array is 0 then the array cannot
>    be expanded. Otherwise  it can.   Such  behavior  forces  people to
>    write difficult to understand/maintain code.
>
> Example 3:
>
>    1 set -u;
>    2 unset A;
>    3 declare -a A=(1); unset A[0];
>    4 echo "s1";
>    5 echo "size: address@hidden";
>    6 echo "s2";
>    7 for e in address@hidden; do echo "e: $e"; done
>
>    The behavior of this example is similar with the behavior of the
>    example 2 above.
>
> I would like to know if these behaviour make sense.
>
> regards
> Cristian
>
>
>
>
>
>
>
>
>


reply via email to

[Prev in Thread] Current Thread [Next in Thread]