2

I've been stuck on this issue around variable expansion in Bash scripts where a multi-word argument surrounded by spaces is getting split into multiple arguments when passed down a chain of functions.

For example:

"this entire string"

becomes:

"this
entire
string"

Extracting out the arrangement of functions out of the code that it's causing the issue in, below is what I could come up with to reproduce the issue:

#!/usr/bin/env bash

# The function that gets called from outside the script
wrapper_function () {
    echo "1: Parameter count: $#"
    for i in "$@"; do
        echo "wrapper_function - $i"
    done
    actual_function $@
}

# The actual low-level function that does something useful
actual_function () {
    echo "2: Parameter count: $#"
    for i in "$@"; do
        echo "actual_function - $i"
    done
}

# Setting the value of the 'problematic' argument
some_string="\"five-1 five-2\""

# Calling the function with a collated set of arguments
wrapper_function "one two three four ${some_string}"

On running this I would get:

1: Parameter count: 1
wrapper_function - one two three four "five-1 five-2"
2: Parameter count: 6
actual_function - one
actual_function - two
actual_function - three
actual_function - four
actual_function - "five-1
actual_function - five-2"

Instead, I expect:

1: Parameter count: 1
wrapper_function - one two three four "five-1 five-2"
2: Parameter count: 5
actual_function - one
actual_function - two
actual_function - three
actual_function - four
actual_function - "five-1 five-2"

Is there anything that I could do to get around this, maybe quoting some arguments or passing them around some other way?

I found a similar question that this one might look like a duplicate of but I think it's not.

2 Answers 2

1

Just do proper quoting

#! /bin/bash

function level3 {
    printf -- "-- Level3 ------\nGot %d arguments:\n" $#
    for arg in "$@"
    do
        printf "%s\n" "$arg"
    done
    printf -- "--------\n"
}

function level2 {
    printf -- "-- Level2 ------\nGot %d arguments:\n" $#
    for arg in "$@"
    do
        printf "%s\n" "$arg"
    done
    printf -- "--------\n"
    level3 "$@"
}

function level1 {
    printf -- "-- Level1 ------\nGot %d arguments:\n" $#
    for arg in "$@"
    do
        printf "%s\n" "$arg"
    done
    printf -- "--------\n"
    level2 "$@"
}

level1 "$@"

Gives:

>>./quotes a bc "\"abc def\""
-- Level1 ------
Got 3 arguments:
a
bc
"abc def"
--------
-- Level2 ------
Got 3 arguments:
a
bc
"abc def"
--------
-- Level3 ------
Got 3 arguments:
a
bc
"abc def"
--------
2
  • Alright, so now I have two potential solutions, and this might be the correct one. I'll try it out and let you know.
    – myterminal
    Feb 14, 2020 at 21:54
  • Update: It works, thank you so much!
    – myterminal
    Feb 14, 2020 at 22:17
1

Well, after a lot of playing around, I finally solved it myself and I think this could really help someone else coming across this kind of an issue. The solution basically needed two changes:

  1. The function wrapper_function needed to be called with two arguments instead of one. I would pass the first set of 'usual' arguments as a single string and then the 'problematic' argument as the second.
  2. The function actual_function needed to pass around the two received arguments down the chain separately instead of referring the entire collection as $@.

The code now looks like:

#!/usr/bin/env bash

# The function that gets called from outside the script
wrapper_function () {
    echo "1: Parameter count: $#"
    for i in "$@"; do
        echo "wrapper_function - $i"
    done
    actual_function $1 "$2"
}

# The actual low-level function that does something useful
actual_function () {
    echo "2: Parameter count: $#"
    for i in "$@"; do
        echo "actual_function - $i"
    done
}

# Setting the value of the 'problematic' argument
some_string="\"five-1 five-2\""

# Calling the function with a collated set of arguments
wrapper_function "one two three four" "${some_string}"

And the output though a little different seems to do what I intended to do:

1: Parameter count: 2
wrapper_function - one two three four
wrapper_function - "five-1 five-2"
2: Parameter count: 5
actual_function - one
actual_function - two
actual_function - three
actual_function - four
actual_function - "five-1 five-2"

I hope someone would find this useful.

2
  • Not clear what your problem is but this is likely not the answer
    – xenoid
    Feb 14, 2020 at 21:35
  • Well, it seems to work for me. I have all the code pasted in the answer and it does prove that too that it at least works for my specific use-case. (From a naive bash scripter) Any enlightenment would be appreciated.
    – myterminal
    Feb 14, 2020 at 21:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .