1

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.

New contributor
myterminal is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
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"
--------
  • 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 at 21:54
  • Update: It works, thank you so much! – myterminal Feb 14 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.

New contributor
myterminal is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
  • Not clear what your problem is but this is likely not the answer – xenoid Feb 14 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 at 21:37

Your Answer

myterminal is a new contributor. Be nice, and check out our Code of Conduct.

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.