1

I am trying to write a simple function in bash which takes 2 arguments, a string and an array.

At the very beggining of the function I check for the number of arguments

function is2args() {
    if [ $# -le 1 ]; then
        return 1
    fi

    return 0
 }

I then attempt the following

arr=()
is2args "" "${arr[@]}" # returns 1

This triggers the if statement as bash for some reason thinks there is only one argument however if the list is an empty string it works

arr=()
is2args "" "" # returns 0

or filled with elements

arr=(
     "element"
)
is2args "" "${arr[@]}" # returns 0

and default to an empty string

arr=()
is2args "" "${arr[@]:-""}" # returns 0

I don't quite understand what is going on. It is my understanding this is the correct way to pass a list however it seems that an empty list is ignored for whatever reason.

Should I really be setting a default empty string every time I send through an array or is there a way to catch this in the function itself?

12
  • 1
    If you expand an array onto a command line, the array's elements become your subsequent arguments. You pass a zero-argument array after a single fixed command, you have one argument total. You pass a three-argument array, you have four arguments total. That's expected behavior -- work with the language, not against it. Commented Sep 6, 2017 at 20:23
  • 1
    There is no such thing as an array value in shell. You can pass a string that names an array variable, or you can pass the elements of the array as one or more arguments. Commented Sep 6, 2017 at 21:12
  • @charles If what you are saying it true it makes me.very confused because I have written functions before where I pass 2 arrays to a function and I am able to distinguish between both. Shouldn't they all mix in one big bag and not be possible to distinguish? Commented Sep 7, 2017 at 6:26
  • @ByteFlinger, yes, that is in fact what happens. If you're distinguishing them, you're presumably doing something like ${array[*]}, converting them to strings -- in which case they aren't arrays any longer at all. Commented Sep 7, 2017 at 12:11
  • @ByteFlinger, if you can show such a function, I can describe its actual behavior (and provide some sample data with which it will misbehave to demonstrate why that behavior is buggy/wrong). Commented Sep 7, 2017 at 12:12

3 Answers 3

3

This is expected. The "${arr[@]}" form expands to put each element of the array on the command line, and there are no elements in the array.

The "${arr[*]}" form will expand to one string consisting of the elements concatenated with a space (by default).

You might try this to test the arguments passed:

$ nargs () {
  local i=0 
  for arg do printf "%d\t>%s<\n" $((++i)) "$arg"; done
}

$ nargs foo "${arr[@]}"
1   >foo<
$ nargs foo "${arr[*]}"
1   >foo<
2   ><

$ arr=(1 2 3)
$ nargs foo "${arr[@]}"
1   >foo<
2   >1<
3   >2<
4   >3<
$ nargs foo "${arr[*]}"
1   >foo<
2   >1 2 3<

$ IFS=,
$ nargs foo "${arr[*]}"
1   >foo<
2   >1,2,3<
Sign up to request clarification or add additional context in comments.

1 Comment

Everything here is true, but a string is a less expressive data structure than an array -- encouraging folks to compress one into the other without any means of encoding or escaping is encouraging them to, at best, restrict the range of data their code can accurately process.
3

You might want to pass the name of the array (note that the below requires bash 4.3):

myfunc() {
  local arg1=$1
  local -n array=$2
  printf "first arg: %q\n" "$arg1"
  echo array:
  declare -p array
}

arr=()
myfunc "foo" arr

1 Comment

This looks good to me (other than the print/printf bug, now fixed). Might also choose a less likely name for the local -- myfunc foo array wouldn't behave so well. (I tend to use declare -n myfunc_array=$2). Indicating explicitly that bash 4.3 is required wouldn't hurt.
2

If you want to pass a fixed argument and an array, shift the first element off; what remains in "$@" is the contents of your array. If the array is empty, $@ will be empty too. (If $# is 0 after the shift, that means your array's length was 0 -- or no array was passed; the two cases are precisely identical).

myfn() {
  local string_arg=$1; shift || { echo "ERROR: Initial argument missing" <&2; return 1; }
  local -a array_args=( "$@" )

  echo "Initial argument is: $string_arg"
  echo "Array has length ${#array_args[@]}"
  echo "Array elements:"
  printf ' - %q\n' "${array_args[@]}"
}

array=( "first array member" "second array member" )
myfn "first argument" "${array[@]}"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.