user3081519
user3081519

Reputation: 2869

Strange characters appearing in bash variable expansion

Trying to do the following on contos7 works as I expect:

pod_in_question=$(curl -u uname:password -k very.cluster.com/api/v1/namespaces/default/pods/ | grep -i '"name": "myapp-' | cut -d '"' -f 4)

echo "$pod_in_question"

curl -u uname:password -k -X DELETE "very.cluster.com/api/v1/namespaces/default/pods/${pod_in_question}"

However, trying the same thing on MacOS (10.12.1) yields:

curl: (3) [globbing] bad range in column 92

When I try to curl the last line with a -g option it substitutes with a malformed name such as: myapp-\\x1b[m\\x1b[Kl1eti\

The echo statement would always execute just fine and show something like myapp-v7454 which I later want to put into the last curl statement. So where are these other characters coming from?

Upvotes: 2

Views: 849

Answers (2)

hmedia1
hmedia1

Reputation: 6180

A robust solution - Basic cURL CLI debugging.

This answer is revised after it's been identified that the issue for the OP relates to curl applying color output.

There's a proposed answer which explains clearly what the embedded special characters meant, and instructions to override the grep behaviour to not output color. Certainly this is a good practise for grep use in piping. There are however a number of best practises that can help diagnose this or a similar issue with cURL and ultimately lead to the most robust solution.

Re-creating the problem

  1. Assuming it's a JSON Content-Type, we use echo {'"name": "myapp-7414"'} to simulate the output from cURL
  2. We filter the text and set a variable with it that we use in a cURL command
  3. We force grep to output color, since it doesn't normally by default when outputting to a tty.

    Recreation:

    myvar=$(echo {'"name": "myapp-7414"'} | grep --color=always -i '"name": "myapp-' | cut -d '"' -f 4)
    curl "https://www.google.com/${myvar}"
    

    Output:

    curl: (3) [globbing] bad range in column 32

First up: '{}' are special characters to cURL, period.

  • The best practise for URL syntax in cURL:

    • If Variable Expansion is required:

      • Apply the -g switch to disable potential globbing done by cURL
    • Otherwise:

      • Use $variable as part of a "quoted" url string, instead of ${variable}

Second: In addition to -g, we add --libcurl /tmp/libcurl so we can get some insight into what cURL is seeing.

   Recreation with -g and --libcurl:

  • curl -g --libcurl /tmp/libcurl  "https://www.google.com/${myvar}"

    Output:

    <p>Your client has issued a malformed or illegal request <ins>That’s all we know.

Perfect, at least now everything is getting to the server and back! Let's see what cURL sent out to the server:

cat /tmp/libcurl

Surely enough we find this line: (note the bold part).

curl_easy_setopt(hnd, CURLOPT_URL, "https://www.google.com/myapp-\033[m7414");

So we know that:

  1. The shell is doing something strange with our variable.
  2. cURL knows not to try glob once we send the -g switch. That way - If there is an error with the shell variable, we can actually see what it is. We shouldn't be debugging a globbing error if we're not trying to use URL Ranges.
  3. The special characters are colors. They represent the --color=always that we added to simulate the OPs environment.

At this point. Since it looks like we're working with JSON data, why not just use a widely available, high performance JSON parsing tool. That has a number of benefits, including:

  • Not relying on any environment that could affect string filtering
  • Can request the data we want (aka. "name")
  • The app name "myapp" can change and we won't have to re-write the code to retrieve it.
  • It's cleaner and accounts for things I haven't considered yet.

If we used jq for example (while we're at it, we don't need the -g switch because we don't need '{}' for the variable because we're already double " the URL):

myvar=$(echo {'"name": "myapp-7414"'} | jq -r .name)
curl --libcurl /tmp/libcurl "https://www.google.com/$myvar"

Now we get:

<p>The requested URL /myapp-7414 was not found on this server. That’s all we know.


Great. It's all working now. It should be obvious that the test URL here being www.google.com is obviously not going to know was myapp-7414 was.

So we've gone from :

  • Globbing bad range, to:
  • Malformed URL, to:
  • URL not found on server.

We could also as suggested elsewhere change the grep output and override it to --color=never (As I have noted: If grep has to be used, the --color=never is a great way to use it as a best practise when piping strings, period.). However, given the portability issues already experienced because of string filtering, and the fact that we are already handed structured data on a plate that can be parsed reliably, the more robust solution would be to do just that, if possible.

Upvotes: 3

paddy
paddy

Reputation: 63481

The substitution you showed at the last part looks like one of your calls injected ANSI escape sequences. It's possible that grep isn't detecting non-TTY output and is colorizing.

On a terminal that supports ANSI escape sequences, your particular codes might not be visible. The codes ^E[m^E[K set the screen mode and clear the current line. That's why you thought the echo command proved your data was correct.

You can examine the raw data with:

echo "$pod_in_question" | hexdump -C

And you should see there are other characters in there which did not appear in your terminal before. When you put these "invisible" codes into the URL, curl tries to encode them and then fails when it encounters a control character (ESC).

The solution is to add the argument --color=never to your grep call, which will disable colorization.

Upvotes: 0

Related Questions