2

I am trying to pass a string array to a function in Bash which results in "syntax error near unexpected token `('"

 #!/usr/bin/env bash                                                                

 function __test() {                                                                
   my_command -c ("a" "b" "c" "d")                                                                                           
 } 

What am I doing wrong here ?

1
  • In brief, you are assuming (a b c d) is an array value, when no such value exists. bash only has array variables, which provide syntax for addressing individual string values using the same variable name. Commented Jan 21, 2017 at 14:21

2 Answers 2

2

You cannot have Bash pass an array by reference, or have it copied as a single entity.

There are two approaches to achieving the desired result.

The first one is copying the data

 function __test()
 {
   declare -a array=("a" "b" "c" "d")
   my_command -c "${array[@]}"
 }

my_command()
{
  # Handle options
  [[ "$1" != -c ]] || shift
  # Get your data back in array form by collecting
  # positional parameters in an array
  declare -a array=("$@")
  # Display element at position 2
  echo "${array[2]}"
  # Display all elements
  echo "${array[@]}"
}

If mycommand is an external program in another language, then you would receive the data as positional parameters.

The second approach is indirection, and will only work if the data is used inside the same Bash script so that the variable can be access in the same scope.

 function __test()
 {
   declare -a array=("a" "b" "c" "d")
   my_command -c array
 }

my_command()
{
  # Handle options
  [[ "$1" != -c ]] || shift
  varname="$1"
  # Access element at position 2
  x="$varname[2]"
  echo "${!x}"
  # Access all elements
  x="$varname[@]"
  echo "${!x}"
}

You need to make sure the variable name used does not contain any unwanted data, or else there could be risks or code injection, so unless the variable name is fully under the control of your program (no chance of user input being included in the variable name), you have to find a way to sanitize it.

Recent variables of bash have a -n option in variable declaration statements (such as local) which you may also want to take a look at, but I would think this is not deployed widely enough to be used except for known configurations.

Please note that I would normally declare all variables local in functions unless I have a specific reason for not doing so, this has been omitted in the code above for clarity purposes.

Sign up to request clarification or add additional context in comments.

9 Comments

Why do you want to add stuff that is not needed in the scope of current question? The question's scope ended at fixing the syntax issue either passing the individual words or as an array both of which I attempted to answer. Appreciate your efforts, but this creates just too much redundancy for such a trivial answer.
@Inian I very much appreciate and find this answer very useful and educational. Clearly my example of using array begs for this answer
Well, it depends how you interpret the OP question, i.e. whether you concentrate on the "syntax error" or "passing an array". I tried to provide ways for the OP to get the data back in array form on the other side of the call, which I (maybe incorrectly) assumed he was trying to do.
@Bren: If you hadn't seen it already. I already updated the mine with arrays,
@Fred: Same as above, my answer already has that way of using the arrays
|
1

It is a syntax violation, just do,

function __test() {                                                                
   my_command -c "a" "b" "c" "d"                                                                                          
} 

to pass the four strings to my_command. The () is a syntax in bash for execution under a sub-shell which is probably not applicable here.

(or) using an array

function __test() { 
   local myArray=("a" "b" "c" "d" )                                                               
   my_command -c "${myArray[@]}"                                                                                         
} 

Your idea of using the array is right, but pass the array as a whole as ${myArray[@]} to the command.

You asked for a getopts to handle this, and here is how you handle it. There are two ways to do this,

To pass the arguments as a single quoted string "a b c d"

usage() { echo "Usage: $0 [-c args]" 1>&2; exit 1; }

[ $# -eq 0 ] && usage

while getopts ":c:" arg; do
  case $arg in
    c)
      IFS=' ' 
      argsC=($OPTARG)
      ;;

    *) 
      usage; exit 0
      ;;
  esac
done

printf "%s\n" "Number of arguments: ${#argsC[@]}"

and running now,

./script.sh -c "a b c d"
Number of arguments: 4
a
b
c
d

and for sending as multiple strings, add the OPTARG value to the array, changing the getopts part alone,

while getopts ":c:" arg; do
  case $arg in
    c)
      argsC+=($OPTARG)
      ;;

    *) 
      usage; exit 0
      ;;
  esac
done

Now running the script,

./script.sh -c "a" -c "b" -c "c" -c "d"
Number of arguments: 4
a
b
c
d
du

Use this [shell check] site for syntactically verifying your scripts.

2 Comments

Ah, right. How can I get that array as an argument of -c option when parsing with getopts ? It only gives me "a" as the argument
Actually never mind, think I know how. Thanks a lot :)

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.