Search code examples
bashjqstring-interpolation

"Unexpected $end" when using bash variables


I've got a fairly simple bash script where I want to enable or disable a jq filter based on the value of an environment variable.

Here's a simplified version of the script:

JSON='{ "key1": { "name":"foo" }, "key2": { "name":"bar" } }'
OPTIONAL_FILTER=""
if [[ "$FLAG" == "value" ]]; then
    OPTIONAL_FILTER='| select(.key == "key2")'
fi
jq 'to_entries[] '${OPTIONAL_FILTER}' | .value.name' <<< $JSON

When I run the script with FLAG="", then everything works as expected and I get the output:

"foo"
"bar"

But if I set FLAG="value", then I get the following error:

jq: error: syntax error, unexpected $end (Unix shell quoting issues?) at <top-level>, line 1:
to_entries[] |
jq: 1 compile error

When I run the same commands in zsh, I do not get this error and I get the expected output of just "foo"

I am not very experienced in shell scripting. I'm guessing that there's a null character or some other input terminator like that being inserted into my string, but I haven't been able to confirm that or to find out a good alternative. It's especially vexing because it seems to be shell-specific. zsh, which I use for most of my testing, handles the code just fine. I only get that error when I attempt to run a .sh script that I see the error.

The ultimate goal is to get the optional filter working correctly. I am open to alternatives if there is a jq-specific option that I've missed. I tried using jq 'to_entries[] $ENV.OPTIONAL_FILTER | .value.name' <<< $JSON, but got jq: error: syntax error, unexpected '$', expecting $end

> bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)

Solution

  • Manipulating program text by string interpolation is almost always best avoided if possible; fortunately, using jq, to my knowledge, it's always not only possible but also quite straightforward.

    In the present case, you could for example use the --arg command-line option, like so:

    jq --arg flag "$FLAG" '
      to_entries[]
      | if $flag == "value" then select(.key == "key2")
        else . end
      | .value.name' <<< $JSON