Bash Expansion Mystery

52 Views Asked by At

This is a very "popular" topic on stackoverflow and unfortunately I haven't been able to figure this out when looking at other questions. Anyways let's consider the following scenario. A function gets a bunch of string arguments like so: some_function "give some" "string arguments" The first thing this function does is put these args into an array so that for example array[0] gives "give some" and array[1] gives "string arguments"

Now lets say I have an array of strings some_array and want to call some_function on it. How do I "transform" that array to make it work?

Here are a few examples which do NOT work:

function print_args() {
  local arr=( "$@" )
  i=0;
  for item in "${arr[@]}"
  do
    echo "[$i] -> '$item'"
    (( i++ ))
  done
}

echo "example"
print_args "this is" "the desired" "behavior"
echo -e "----------------\n"

some_array=( "\"does NOT\""  "\"seem to\"" "\"work\"" )
stringified_array=$(printf "%s " "${some_array[@]}")
echo "stringified array: [$stringified_array]"

echo "1) passing \$some_array"
print_args $some_array
echo -e "---------------------------\n"

echo "2) passing \"\$some_array\""
print_args "$some_array"
echo -e "---------------------------\n"

echo "3) passing \$stringified_array"
print_args $stringified_array
echo -e "---------------------------\n"

echo "4) passing \"\$stringified_array\""
print_args "$stringified_array"
echo -e "---------------------------\n"

and here is the output

example
[0] -> 'this is'
[1] -> 'the desired'
[2] -> 'behavior'
----------------

stringified array: ["does NOT" "seem to" "work" ]
1) passing $some_array
[0] -> '"does'
[1] -> 'NOT"'
---------------------------

2) passing "$some_array"
[0] -> '"does NOT"'
---------------------------

3) passing $stringified_array
[0] -> '"does'
[1] -> 'NOT"'
[2] -> '"seem'
[3] -> 'to"'
[4] -> '"work"'
---------------------------

4) passing "$stringified_array"
[0] -> '"does NOT" "seem to" "work" '
---------------------------

I think I understand 1) and 2) and only tried them out of desperation. I believe I understand 4) as well. Now my big problem is that I don't understand what the heck is going on with 3) and more importantly how I can "stringify" my array so as to achieve what I want. An important note here is that I would like to try and avoid the use of eval.

Thanks!

1

There are 1 best solutions below

1
On BEST ANSWER

You may expect that you are saying with 3):

print_args "does NOT" "seem to" "work"

but in reality 3) is equivalent to:

print_args '"does' 'NOT"' '"seem' 'to"' '"work"'

this is because the unquoted argument variable $stringified_array is split into words on IFS before it is passed to the function. The double quotes within the string is just a part of string and does not work to quote the whitespaces.

Try to see what happens by modifying line #16-#17 in your sample code with:

16c16
< stringified_array=$(printf "%s " "${some_array[@]}")
---
> stringified_array=$(printf "%s#" "${some_array[@]}")
17a18
> IFS=#

By assigning IFS to a delimiter which does not appear in your strings, you will be able to stringify your array safely, although there will be other solutions without stringifying the array.
Hope this helps.