4

I try to pass some parameters to my .bash file.

terminal:

arr=("E1" "E2" "E3")
param1=("foo")
param2=("bar")

now I want to call my execute.bash file.

execute.bash -a ${arr[@]} -p $param1 -c param2

this is my file:

execute.bash:
while getopts ":a:p:c:" opt; do
    case $opt in
        a) ARRAY=${OPTARG};;
        p) PARAM1=${OPTARG};;
        c) PARAM2=${OPTARG};;
        \?) exit "Invalid option -$OPTARG";;
    esac
done

for a in "${ARRAY[@]}"; do
    echo "$a"
done

echo "$PARAM1"
echo "$PARAM2"

But my file only prints:

E1
foo
bar

Whats the problem with my script?

Inian
  • 80,270
  • 14
  • 142
  • 161
Rod Kimble
  • 1,302
  • 3
  • 18
  • 44

3 Answers3

2

Expanding all values in the array using ${arr[@]} expands each value as a separate command-line argument, so getopt only sees the first value as the parameter to the "-a" option.

If you expand using ${arr[*]} then all of the array values are expanded into a single command-line argument, so getopt can see all of the values in the array as a single argument to the "-a" option.

There are a couple of other issues: you need to quote the values on the command line:

< execute.bash -a ${arr[@]} -p $param1 -c param2
> execute.bash -a "${arr[*]}" -p $param1 -c $param2

and use braces around ${OPTARG} in the getopt processing to make it an array assignment:

< a) ARRAY=${OPTARG};;
> a) ARRAY=(${OPTARG});;

after making these changes, I get this output:

E1
E2
E3
foo
bar

which I think is what you are expecting.

sheltond
  • 1,877
  • 11
  • 15
2

You have a problem with passing the array as one of the parameter for the -a flag. Arrays in bash get expanded in command line before the actual script is invoked. The "${array[@]}" expansions outputs words separated by white-space

So your script is passed as

-a "E1" "E2" "E3" -p foo -c bar

So with the getopts() call to the argument OPTARG for -a won't be populated with not more than the first value, i.e. only E1. One would way to achieve this is to use the array expansion of type "${array[*]}" which concatenates the string with the default IFS (white-space), so that -a now sees one string with the words of the array concatenated, i.e. as if passed as

-a "E1 E2 E3" -p foo -c bar

I've emphasized the quote to show arg for -a will be received in getopts()

#!/usr/bin/env bash

while getopts ":a:p:c:" opt; do
    case $opt in
        a) ARRAY="${OPTARG}";;
        p) PARAM1="${OPTARG}";;
        c) PARAM2="${OPTARG}";;
        \?) exit "Invalid option -$OPTARG";;
    esac
done

# From the received string ARRAY we are basically re-constructing another
# array splitting on the default IFS character which can be iterated over
# as in your input example

read -r -a splitArray <<<"$ARRAY"

for a in "${splitArray[@]}"; do
    echo "$a"
done

echo "$PARAM1"
echo "$PARAM2"

and now call the script with args as. Note that you are using param1 and param2 are variables but your definition seems to show it as an array. Your initialization should just look like

arr=("E1" "E2" "E3")
param1="foo"
param2="bar"

and invoked as

-a "${arr[*]}" -p "$param1" -c "$param2"

A word of caution would be to ensure that the words in the array arr don't already contain words that contain spaces. Reading them back as above in that case would have a problem of having those words split because the nature of IFS handling in bash. In that case though use a different de-limiter say |, # while passing the array expansion.

Inian
  • 80,270
  • 14
  • 142
  • 161
0

If I want to export the array MY_ARRAY, I use at caller side:

[[ $MY_ARRAY ]] && export A_MY_ARRAY=$(declare -p MY_ARRAY)

... and at at sub script side:

[[ $A_MY_ARRAY =~ ^declare ]] && eval $A_MY_ARRAY

The concept works for parameters too. At caller side:

SUB_SCRIPT "$(declare -p MY_ARRAY)"

... and at at sub script side:

[[ $1 =~ ^declare ]] && eval $1

The only issue of both solution is, that the variable names are the same at both sides. This can be changed if replacing the variable name before expanding it.

Wiimm
  • 2,971
  • 1
  • 15
  • 25