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:
-H
"Content-Type:
application/json"
instead of (what I expected):
-H
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"