0

Not critical - but I'm trying to get a deeper understanding of bash scripting and this is driving me crazy!

My goal - in a bash script:

  • Define a function that accepts arguments
  • Set a bash variable (CMD) with the function name & arguments
  • Execute it by $CMD

No problem if there is no whitespace in $args - but here's a minimal script to illustrate:

#!/bin/bash
function tstArgs () {
    echo "In tstArgs: ArgCnt:$#; Arg1:[$1]; Arg2:[$2]"
}
ARG1=today
ARG2=tomorrow
CMD1="tstArgs $ARG1 $ARG2"
$CMD1 #Output - As Desired: In tstArgs: ArgCnt:2; Arg1:[today]; Arg2:[tomorrow]

ARGWS1="'today with spaces'"
ARGWS2="'tomorrow with spaces'"
CMD2="tstArgs $ARGWS1 $ARGWS2"
$CMD2 #Output: In tstArgs: ArgCnt:6; Arg1:[today]; Arg2:[with]

#The dream:
ARGARR=($ARGWS1 $ARGWS2)
CMD3="tstArgs ${ARGARR[@]}"
$CMD3 #Output: In tstArgs: ArgCnt:6; Arg1:[today]; Arg2:[with]
#ETC, ETC, ETC...

This doesn't show the COUNTLESS variations I tried - single quotes, double quotes, escaping quotes, changing IFS, using parameter escape operators ${ARG1@Q}, setting args w. echo XXX - and so much more - way too many to include here, but to be clear, I didn't just jump on stackoverflow without first spending HOURS.

Weirdly, I can use params w. whitespace if I call the function directly:

tstArgs $ARG1 $ARG2
#But no variation of anything like:
CMD="tstArgs $ARG1 $ARG2"
$CMD

I'm sure it must be possible, and probably simple - but it's some permutation I just haven't been able to crack.

Of course I can work around it - but I'm stubborn & persistent & hate to give up. If anyone has any insight, I'd be very grateful, and maybe even finally get some sleep...

ChrisNY
  • 3,917
  • 4
  • 27
  • 31
  • 2
    Storing commands as strings doesn't work; variables are for storing data, not executable code. See [BashFAQ #50: "I'm trying to put a command in a variable, but the complex cases always fail!"](http://mywiki.wooledge.org/BashFAQ/050) and [many previous questions here](https://stackoverflow.com/questions/12136948/why-does-shell-ignore-quoting-characters-in-arguments-passed-to-it-through-varia) (but avoid all suggestions involving `eval` -- it is a massive bug magnet). – Gordon Davisson Dec 01 '22 at 19:40
  • 1
    'eval' is a common misspelling of 'evil'. If eval is the answer, surely you are asking the wrong question. See http://mywiki.wooledge.org/BashFAQ/048 – Gilles Quénot Dec 01 '22 at 19:53

1 Answers1

2

Don't put arguments in a string. Put them in an array. Array elements handle spaces much more gracefully:

declare -a ARGS
ARGS+=( "today with spaces" )
ARGS+=( "tomorrow with spaces" )
CMD="tstArgs"
${CMD} "${ARGS[@]}"

Alternatively:

declare -a ARGS
ARGS[0]="today with spaces"
ARGS[1]="tomorrow with spaces"
CMD="tstArgs"
${CMD} "${ARGS[@]}"

Putting quotation marks around ${ARGS[@]} on the last line makes sure that each element of the array is quoted, thus preserving the spaces.

Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
Andrew Vickers
  • 2,504
  • 2
  • 10
  • 16
  • Thanks. I'm marking this as "accepted" - because there doesn't seem to be an easy way to do what I want with Bash, and it's not a big deal. For reference, my motivation was to create a pattern for multiple bash scripts maintained by multiple developers over multiple years. I've seen way too many things "maintained" by changing code in one part but not another, so I wanted to do something like: CMD=tstArgs $ARG1 $ARG2 #(or whatever) echo "About to execute [$CMD] $CMD – ChrisNY Dec 05 '22 at 19:56