Shell – Hold multiple space containing arguments in a single variable

shellshell-scriptvariable

I was trying to debug my shell script:

content_type='-H "Content-Type: application/json"'
curl $content_type -d "{"test": "test"}" example.com

I learned that this does not do what I expect. In this script, the arguments being passed to curl (due to $content_type) are:

  1. -H
  2. "Content-Type:
  3. application/json"

instead of (what I expected):

  1. -H
  2. Content-Type: application/json

I know that I can write the script as follows:

content_type="Content-Type: application/json"
curl -H "$content_type" -d "{"test": "test"}" example.com

and it would work this way but I am wondering if it is possible to hold multiple space containing arguments in a single variable.

With the second (working) version, if I want to exclude the content type, I need to remove two things: -H and $content_type.

However, I want to know if it is possible to put the option and its value into a single entity, so that excluding/including the content type will result in removing/adding a single entity.

Using arrays is not portable because there are no arrays in POSIX.

Why does the first method behave that way?

The reason is word splitting. When we define the content_type as follows:

content_type='-H "Content-Type: application/json"'

it has the value:

-H "Content-Type: application/json"

When we reference that variable without quotes (that is, $content_type, instead of "$content_type"), the expanded value becomes a subject of word splitting. From word splitting page of bash manual:

The shell scans the results of parameter expansion, command
substitution, and arithmetic expansion that did not occur within
double quotes for word splitting.

From the same page:

The shell treats each character of $IFS as a delimiter, and splits the
results of the other expansions into words using these characters as
field terminators. If IFS is unset, or its value is exactly
<space><tab><newline>, the default …

So, -H "Content-Type: application/json" is splitted by using <space><tab><newline>as delimiters. That gives us:

-H
"Content-Type:
application/json"

Best Answer

There is one standard array like structure in the standard shell. It is the positionnal parameters. So a possible solution is to use set -- … and "$@". In your case you would do:

set -- -H "Content-Type: application/json"
curl "$@" -d "{"test": "test"}" example.com

Note that this limit you to only one available array. You can't have one for curl and the other for another program for example. And of course it trashes the arguments to your script.