5

The following post solution works as expected:

Therefore - from his answer:

function copyFiles() {
   arr=("$@")
   for i in "${arr[@]}";
      do
          echo "$i"
      done

}

array=("one 1" "two 2" "three 3")

copyFiles "${array[@]}"

The reason of this post is what if the following case happens:

copyFiles "${array[@]}" "Something More"
copyFiles "Something More" "${array[@]}"

Problem: I did do realize about the arguments sent, when they are received how parameters in the function - they are practically merged - so $1 and $2 does not work as expected any more, the "array" argument integrates with the other argument

I already did do a research:

Sadly typeset -n does not work

and in:

does not work as expected - in that answer there is a comment indicating an issue - with a link with a demo testing/verification - about the array size (${#array[@]}) being different within the function.

So how accomplish this goal?

2 Answers 2

6

It is not possible to pass an array as argument like that. Even though it looks like you do that, it does not work as you expect it

Your shell (e.g. here: bash) will expand "${array[@]}" to the individual items before executing the function !

So, this

copyFiles "Something More" "${array[@]}"

will actually call

copyFiles "Something More" "one 1" "two 2" "three 3"

So, inside the function it is not possible to distinguish the array from other arguments.

(You could add a reference to an array, but I would argue against using it, as it doesn't seem to be very portable, also you won't want to mix scopes if not necessary).


You can use shift, e.g.

copyFiles() {
    var1=$1
    shift
    for i in "$@"; do ... ; done
}

(Note, that arr=("$@") is superfluous, $@ is already an array, you don't even need to specify "$@", you could also use for i; do ...; done)

or parse arguments with something like getopts.

2

You can't do that directly, not very well anyway. Arrays really aren't first-class objects in Bash, and if you need to do something like this, you might want to really consider switching to a real programming language...

The core issue is that "${array[@]}" just expands to (a list of) all the elements of the array (forgetting about the indexes!), instead of actually giving the array itself.

Of course there are some workarounds, not that I'm sure I'd call them very good. But you could...

  1. use some separator:

    func "non-array" "arguments" :: "${array[@]}" 
    

    This is essentially what e.g. GNU Parallel does, but the downside is that you need to decide on some value that can't be used as an actual argument. (Or some way of escaping it, which might be worse.) You'll have to scan the list of arguments to find the separator.

  2. add arguments to tell the lengths of the lists:

    func 1 "non-array argument" "${#array[@]}" "${array[@]}" 
    

    That should be general enough to pass any valid value, but if it starts giving you traumatic flashbacks of manual memory handling in C, I don't blame you. Parsing that on the other end is also left as an exercise for anyone crazy enough to want to do it.

  3. pass the array by name instead.

    You probably want to use namerefs, e.g.:

    fun() {
        local _arrayname="$1"
        local -n _ref="$1"
        printf "array '%s' has %d members\n" "$_arrayname" "${#_ref[@]}"
        printf "member at index #2 is '%s'\n" "${_ref[2]}"
    }
    asdf=(one too tree)
    fun asdf
    

    You could try to use the ${!p} indirection syntax too, but IIRC, indexing an array using that syntax is harder. Of course, both are non-portable, so you may end up stuck with Bash, but namerefs are supported in Bash since at least version 4.4, so that shouldn't be a problem in most cases.

    The problem with namerefs is the clashing namespaces, you can't pass arrays called _arrayname or _ref to the above function.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .