Computernerd
Computernerd

Reputation: 7766

Declare array but not define the array ( Shell scripting)

There are alot of guides, guides out there which show how to declare and define an array

foo[0] = "abc" 
foo[1] = "def"

What i am trying to achieve is to declare an array but not define it because it does not have to be define immediately , in most programming languages it will look something like this

int bar[100];

Is this possible in shell scripting lanuage ??

Upvotes: 1

Views: 229

Answers (2)

Beni Cherniavsky-Paskin
Beni Cherniavsky-Paskin

Reputation: 10039

[answering about bash, not sure how portable arrays are between other bourne shells]

The arr[index] assignment syntax can only define arrays of 1 or more elements (and is tiresome when building long arrays). I generally recommend arr=(...) syntax.

  • Do you want an empty array of 0 elements?
    => Use empty_array=() syntax, or local empty_array=() inside shell functions.

  • Do you really care about declaring but not initializing it?
    => That's possible with declare -a uninitialized_array aka local -a uninitialized_array.

    But AFAICT it's not very useful, especially with arrays — it behaves almost same as empty_array, and won't even catch use-before-initialize bugs.


See this answer as a jupyter notebook (with "bash kernel")

There are several aspects here and to separate them we'll need multiple test variables:

empty_array=()
declare -a declared_empty_array=()
declare declared_empty_array2=()
declare -a uninitialized_array
array_with_empty_string=('')
empty_string=''
declare uninitialized_string

debug tips

declare -p is useful to get full¹ metadata on variables. It dumps the commands you could use to re-create variables with same attributes (at least if you start from a blank shell).

$ declare -p empty_array declared_empty_array declared_empty_array2 uninitialized_array array_with_empty_string empty_string uninitialized_string undefined
declare -a empty_array=()
declare -a declared_empty_array=()
declare -a declared_empty_array2=()
declare -a uninitialized_array
declare -a array_with_empty_string=([0]="")
declare -- empty_string=""
declare -- uninitialized_string
bash: declare: undefined: not found

So, first thing we can confirm is -a is entirely optional when assigning array value =(...).
declared_empty_array2 is same as declared_empty_array — both are arrays with empty values.

¹ One thing it won't tell: whether it's local. But local -p would.

But you'll see some distinctions bash makes won't matter in practice.
I'll mostly want to check how variable expansion behaves. I'll be using ruby to print the arguments a command recieves:

$ ruby -e 'p ARGV'     # just to show how it reports 0 arguments...
[]
$ ruby -e 'p ARGV' ''  # ...vs 1 argument which is an empty string
[""]

declare makes variables local!

I'll warn you right away not to think of declare builtin as "declaration".
Yes it does that, and it has advanced uses like readonly vars, associative arrays (out of scope here) etc.
But for our needs here, its most visible effect is making the listed names local inside a shell function (unless you use -g flag).
Inside functions, it's clearer if you use the (mostly equivalent) local spelling instead of declare.

arrays vs. regular string variables: quite weakly typed

We're talking here about "indexed arrays". bash also has "associative arrays" (which really need -A declaration) but off-topic for this question.

Reminder: bash has essentially 3 expansion modes:

  • unquoted $var, ${array[3]}, ${array[*]}, $* all do word splitting resulting in 0 to many arguments, even from a regular non-array var.
  • quoted "$var", "${array[3]}", "${array[*]}", "$*" always result in 1 argument (which might be empty string), even from an array.
  • one-to-one "${array[@]}", "$@" which results in exactly 1 argument per array element. This is the only mode that can distinguish single "foo bar" vs two "foo" "bar", and is the whole point of using real arrays. A pity it's so verbose ☹

You can think of all regular string variables as "wannabe" arrays of 1 element.

Both declare -a and =() syntaxes mark a variable as an array, and declare -p will confirm that, but it almost doesn't matter!
You can freely apply array expansions to string variables and vice versa! And string vars promote to an array as soon as you assign to some other var[index], use +=(...) operator etc.

Using string in array expansion:

$ string1=foo
$ array2=(foo bar)
$ ruby -e 'p ARGV' "${string1[@]}"
["foo"]
$ ruby -e 'p ARGV' "${array2[@]}"
["foo", "bar"]

Using arrays in string expansion behaves as if you accessed first element [0]:

$ ruby -e 'p ARGV' "$empty_array"
[""]
$ ruby -e 'p ARGV' "${empty_array[0]}"
[""]
$ ruby -e 'p ARGV' "$string1"
["foo"]
$ ruby -e 'p ARGV' "$array2"
["foo"]
$ ruby -e 'p ARGV' "${array2[0]}"
["foo"]

uninitialized/unset/unbound variables

For historical reasons, Bourne-derived shells default to silently tolerating expansion of unknown variables ☹

  • When treated as regular variable, by default they silently expand to empty string, but you can enable stricter set -u mode to complain.
  • When expanded as an array, they behave as empty arrays — and set -u doesn't seem to matter?!?
$ ruby -e 'p ARGV' "$undefined"
[""]
$ ruby -e 'p ARGV' "${undefined[@]}"
[]

$ set -u

$ ruby -e 'p ARGV' "$undefined"
bash: undefined: unbound variable
$ ruby -e 'p ARGV' "${undefined[@]}"
[]

$ set +u

declare without value keeps it unset! Is that useful??

You've asked about declaring like int bar[100]; would in C, without initializing a value. Yes you can do that declare -a arr (but can't set size 100). What does that achieve?

  • it makes it local — but so would declare arr=().
  • it marks it as an array — but as we said it's pretty weak type.
  • 💡 set -u mode could catch bugs where it's set conditionally and some code path forgot to set it before use!
    ALAS, array expansions seem allowed ☹
    AND AFAICT it doesn't matter whether we declare it with -a, without -a, or don't declare at all. It only matters which expansion syntax we use — when accessed as array, it'll never cause errors.

=> I don't see any benefit to "declare array without defining". Can always initialize as empty =().

$ ruby -e 'p ARGV' "$uninitialized_string"
[""]
$ ruby -e 'p ARGV' "${uninitialized_string[@]}"
[]

$ set -u

$ ruby -e 'p ARGV' "$uninitialized_string"
bash: uninitialized_string: unbound variable
$ ruby -e 'p ARGV' "${uninitialized_string[@]}"
[]

$ set +u
$ ruby -e 'p ARGV' "$uninitialized_array"
[""]
$ ruby -e 'p ARGV' "${uninitialized_array[@]}"
[]

$ set -u

$ ruby -e 'p ARGV' "$uninitialized_array"
bash: uninitialized_array: unbound variable
$ ruby -e 'p ARGV' "${uninitialized_array[@]}"
[]

$ set +u

Upvotes: 0

rccoros
rccoros

Reputation: 590

You can do that using bash declare statement

declare -a bar

And then you can add values using

bar[0]=1
bar[1]=2
bar[2]=3

And print it using

echo ${bar[0]}
echo ${bar[1]}
echo ${bar[2]}

to output

1
2
3

You can read more here

Upvotes: 2

Related Questions