-1

I have a function that takes arguments in different combinations. How they are setup depends on other conditions so I created a method to set them up.

#!/bin/bash

myFunc() {
    while getopts m:t::T::rp:: arg; do
        case "$arg" in
            m)  log_msg="$OPTARG";;
            t)  timeout="$OPTARG";;
            T)  timeout_msg="$OPTARG";;
            r)  retry="yes";;
            p)  postpone="$OPTARG";;
            *)  die "invalid arguments $*";;
        esac
    done

    #doStuff...
}

setupOpts() {
    local name="$1"
    local param="$2"
    local err_msg="$3"
    if someTest "$name"; then
        # case all
        echo "-t 9 -T "$err_msg" -p "$name" -r"
    elif someOtherTest "$name" "$param"; then
        echo "-t 120 -T "$err_msg" -p "$name""
    else
        echo "-t 300 -T "$err_msg""
    fi
}

mainFunc() {
   # some stuff...
   opts="$(setupOpts "$someName" "$someParam" "Some Err Msg")"
   myFunc -m "Some Msg" "$opts"
}

I not manage to get this working. E.g. when case all (see comment in code) is true:

opts="$(setupOpts "peter-pan" "$someParam" "Some Err Msg")"
# -> results in: myFunc -m "some Msg" -t 9 -T "Some Err Msg" -p "peter-pan" -r

However, the arguments behind -t and following are not parsed corretly. I guess it has something to do with quoting and word splitting.

Depending on quotes:

myFunc -m "Some Msg" "$opts":

timeout=9 -T "Some Err Msg" -p "peter-pan" -r
timeout_msg=
postpone=
retry=

or

myFunc -m "Some Msg" $opts:

timeout=9
timeout_msg=Some
postpone=
retry=

Correct would be

timeout=9
timeout_msg=Some Err Msg
postpone=peter-pan
retry=yes

I have tried different other things like using escaped quotes (echo "-t 9 -T "\"Some Err Msg\"" -p "$argOne" -r") or using echo without surrounding quotes.

Is this somehow doable using bash or am I hitting a limit here?

PS: Setting things up in myFunc would work as well but that's not the solution to my answer. I would like to know if this approach would work with bash somehow.

Chris
  • 5,109
  • 3
  • 19
  • 40
  • please update the question with the details to reproduce your issue, for starters: the definitions of `someTest` and `someOtherTest` – markp-fuso Sep 12 '21 at 17:04
  • Both are functions that check certain conditions and return 0/1 depending on that conditions, e.g. if a vm can be redeployed. I don't see how the exact definition of them would serve the question. The output of `setupOpts() ` should change depending on what condition is true or default to the `else` part. You can use any test for that, e.g. if the name is a certain string. – Chris Sep 12 '21 at 17:41
  • yes, I understand what the functions are supposed to do; I was pointing out that it's harder for people to reproduce the issue let alone come up with suggested solutions when the sample code fails to run (in our environments); regardless, assuming `setupOpts()` is functioning as expected and the only issue is with the quoting/word-splitting when calling `myFunc()` ... my answer addresses this issue; alternatively ... – markp-fuso Sep 12 '21 at 19:05
  • a google search on `bash command line args as array` brings up many hits on this topic, eg, [this](https://stackoverflow.com/questions/12711786) and [this](https://stackoverflow.com/questions/17219453) – markp-fuso Sep 12 '21 at 19:06
  • Thanks for your support. I will take your point into account and post an copy/paste example next time. Regarding the google search: I did an extensive search but didn't find anything that would work but I found some hints that it might not even work due to bash limitations. I didn't search for `bash command line args as array` since I didn't have that idea and thus posted this question to ask if it is even possible (see PS). And as you can see from the accepted answer simply turning the args into an array doesn't worked, I needed to combine it with a nameref. – Chris Sep 13 '21 at 10:38

2 Answers2

2

Due to complexities of quoting, you can use nameref combined with arrays :

#!/usr/bin/env bash

myFunc() {
    while getopts m:t::T::rp:: arg; do
        case "$arg" in
            m)  log_msg="$OPTARG";;
            t)  timeout="$OPTARG";;
            T)  timeout_msg="$OPTARG";;
            r)  retry="yes";;
            p)  postpone="$OPTARG";;
            *)  die "invalid arguments $*";;
        esac
    done

    #doStuff...
}

setupOpts() {
    local name="$1"
    local param="$2"
    local err_msg="$3"
    declare -n inner_opts=$4
    if someTest "$name"; then
        # case all
        inner_opts=(-t 9 -T "$err_msg" -p "$name" -r)
    elif someOtherTest "$name" "$param"; then
        inner_opts=(-t 120 -T "$err_msg" -p "$name")
    else
        inner_opts=(-t 300 -T "$err_msg")
    fi
}

mainFunc() {
   # some stuff...
   local outer_opts
   setupOpts "someName" "someParam" "Some Err Msg" outer_opts
   myFunc -m "Some Msg" "${outer_opts[@]}"
}

mainFunc
Philippe
  • 20,025
  • 2
  • 23
  • 32
0

Without the definitions of someTest and someOtherTest I'm unable to reproduce OP's output so fwiw ...

It looks (to me) like the setupOpts() function generates a set of command line options that are to be passed to the myFunc() function. Assuming setupOpts() generates the correct output, I'm guessing one solution would be store the output from setupOpts in an array, eg:

opts=( $(setupOpts "peter-pan" "$someParam" "Some Err Msg") )

The main call to myFunc() then becomes:

myFunc -m "Some Msg" "${opts[@]}"
markp-fuso
  • 28,790
  • 4
  • 16
  • 36