0

First I've read about passing arrays in general -- all examples I saw first created temporary variable for array and then passed it. Taken from https://stackoverflow.com/a/26443029/210342

show_value () # array index
{
    local -n myarray=$1
    local idx=$2
    echo "${myarray[$idx]}"
}

shadock=(ga bu zo meu)
show_value shadock 2

Is there a way to pass array directly as literal, i.e. without creating temporary variable?

I tried naive approach simply substituting the name with data, but I syntax error on "(".

Update:

I use openSUSE Leap 15.3 with bash 4.4. The above code of course works, but I would like to change the call into:

show_value (ga bu zo meu) 2

i.e. pass array data directly (without using extra variable).

greenoldman
  • 16,895
  • 26
  • 119
  • 185
  • 1
    What do you mean by "literal"? `declare -p shadock`? According to chepner, "[There is no such thing as an array literal in bash](/q/50033402/#comment87086095_50033402)". – wjandrea Nov 22 '21 at 17:42
  • 1
    What problem are you trying to solve by doing this? – wjandrea Nov 22 '21 at 17:46
  • 1
    `syntax error on "("` -- what shell are you using to run this code? `sh` or `bash`? – glenn jackman Nov 22 '21 at 17:51
  • Specifically, which temporary variable are you concerned about? – glenn jackman Nov 22 '21 at 17:51
  • 1
    There is nothing wrong with this code. I tested and got `zo`. – dan Nov 22 '21 at 17:53
  • Name refs were added in bash version 4.3. Which version of bash do you have? Are you on MacOS? – dan Nov 22 '21 at 17:59
  • 2
    The arguments to a function (or command, or whatever) are, by definition, *strings*. You cannot pass any other sort of data structures as arguments. So the choices for "passing an array" are, more or less: pass the elements (each as a separate argument) and have the function treat the arg list (or part of it) as an array; pass the *name* of an array variable (what's done in the example you give); or to somehow convert ("pickle") the array to a single string, and have the function parse that string out into separate elements. – Gordon Davisson Nov 22 '21 at 18:12
  • 1
    what's wrong with using `local -n myarray` (aka `nameref`; aka `temp variable`)? what are you trying to accomplish that this doesn't work? – markp-fuso Nov 22 '21 at 18:13
  • @markp-fuso, sorry, I simply I don't understand what you are asking. – greenoldman Nov 22 '21 at 18:17

3 Answers3

2

If you want to change the order of the arguments:

show_value () #  index array_element [...]
{
    local idx=$1
    local -a myarray=("${@:2}")
    echo "${myarray[$idx]}"
}

then

shadock=(ga bu zo meu)
show_value 2 "${shadock[@]}"   # => zo

If you want to keep the index as the last argument, then

show_value () #  array_element [...] index
{
    local -a myarray=("${@:1:$#-1}")
    local idx=${!#}
    echo "${myarray[$idx]}"
}
show_value "${shadock[@]}" 2   # => zo

local -n myarray=$1 is certainly much tidier than all that, isn't it? It will also be faster and more memory efficient -- you don't have to copy all the data.

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
1

Function arguments are not structured enough to handle nested data structures like arrays. Arguments are a flat list of strings, that's it.

You can expand the array inline:

show_value "${shadock[@]}" 2

However the delimiters are lost. There's no way to know where the array starts and end since it expands to:

show_value ga bu zo meu 2

You'll have to figure out the array bounds yourself. Options include:

  • If the command has only a single array parameter, make it the last one. This is what many traditional UNIX tools that take multiple file names do. Examples: ls <file>..., chmod <mode> <file>....

  • You can put the array in the middle if there's a fixed number of arguments preceding/following it such that you can unambiguously determine where the array is. Example: cp <file>... <dir>.

  • If you have multiple arrays, you can ask users to separate them with --. Many of git's more complicated subcommands do this.

I'd caution against taking an array name as an argument. That's a very shell-specific technique. It would make it hard to turn the function into a full-fledged script or binary down the road.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • Thank you for the explanation and the tips, I indeed reverse the order of the arguments, so the tail will become the array. – greenoldman Nov 22 '21 at 18:16
1

Can you consider this syntax :

show_value () # array index
{
    local -a myarray=$1
    local idx=$2
    echo "${myarray[$idx]}"
}

show_value "(ga bu zo meu)" 2

For security reasons, you need to be sure of the contents of the array you pass into the function.

Philippe
  • 20,025
  • 2
  • 23
  • 32