0

I'm trying to build a function that returns arguments for curl:

get_common_curl_headers() {
  local crash_path="$1"

  echo -n \
    "-H 'Accept: text/plain'" \
    "-F 'model=<$crash_path/model'" \
    "-F 'version=<$crash_path/fw_version'"

  echo -n ' '
}

I'd like the returned file paths to be quoted as $crash_path variable might contain spaces. I then use this function like this:

send_app_crash() {
  local crash_path="$1"
  local common_headers

  common_headers=$(get_common_curl_headers "$crash_path")
  [ $? -ne 0 ] && return 1

  curl -vk -X POST \
    $common_headers\
    "$SERVER_URL"
}

I use the variable $common_headers unquoted here, as I want the arguments to be expanded, which to my understanding should work. However, the command results in this error:

curl: (6) Couldn't resolve host 'text'
curl: (26) couldn't open file "/tmp//model'"

Weirdly, there's an extra single quote at the end of the file name, which should have been removed by the shell. I'm not sure what the first error means as well, as Accept: text/plain seems to be properly quoted (see below).

Running the command manually (i.e. doing set -x before the command appears inside the script and piping it to sh) works as intended:

curl -vk -X POST -H 'Accept: text/plain' -F 'model=</tmp//model' -F 'version=</tmp//fw_version' 192.168.2.100:8888

1 Answer 1

1

How to debug

By just adding set -x before your curl command, you'd see this:

curl -vk -X POST -H ''\''Accept:' 'text/plain'\''' -F ''\''model=<some-crash-path/model'\''' -F ''\''version=<some-crash-path/fw_version'\''' example.com

And now let's do some magic trick:

$ printf "%s\n" curl -vk -X POST -H ''\''Accept:' 'text/plain'\''' -F ''\''model=<some-crash-path/model'\''' -F ''\''version=<some-crash-path/fw_version'\''' example.com
curl
-vk
-X
POST
-H
'Accept:
text/plain'
-F
'model=<some-crash-path/model'
-F
'version=<some-crash-path/fw_version'
example.com

This printf command prints every argument in a separate line, which can help you see that instead of e.g. Accept: test/plain, you generated 2 arguments: 'Accept: and text/plain'.

So curl sees it as -H 'Accept: (i.e. the header starts with ' and does not include the intended value), and the next argument is text/plain', which is not prefixed by any option, so curl thinks it's the URL, which is why it says text is not a host.

It also explains why it can't find /tmp/model' (with ' at the end).

How to fix

Instead of using

curl -vk -X POST $common_headers "$SERVER_URL"

Just add eval:

eval curl -vk -X POST $common_headers "$SERVER_URL"

For your purposes, eval is equivalent to piping to sh.

The right way

I would generally advise against using eval (or piping to sh) for anything but throw-away code.

How would you do it otherwise?

Option 1 - inline get_common_curl_headers() inside send_app_crash(). That's what I prefer to do - introduce functions which wrap commands rather than generating arguments in a separate function.

send_app_crash() {
    local crash_path="$1"
    curl -vk \
        -X POST \
        -H "Accept: text/plain" \
        -F "model=<$crash_path/model" \
        -F "version=<$crash_path/fw_version" \
        "$SERVER_URL"
}

Option 2 - Generate the arguments in a global array:

get_common_curl_headers() {
    local crash_path="$1"

    COMMON_HEADERS=( \
        -H "Accept: text/plain" \
        -F "model=<$crash_path/model" \
        -F "version=<$crash_path/fw_version" \
    )
}

send_app_crash() {
    local crash_path="$1"
    local common_headers

    get_common_curl_headers "$crash_path"
    [ $? -ne 0 ] && return 1

    curl -vk -X POST "${COMMON_HEADERS[@]}" "$SERVER_URL"
}

Note the quoting of the array dereference, and note that you can't return arrays.

The main problem with this approach is that it uses global variables, which is why it's less preferable.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.