1

Using bash, I have a need to echo a series of commands before I run them. Take this example, which is nice and easy and works as expected. Note that all code examples have been run through ShellCheck which reports no issues detected.

OUTPUTS_CMD=(aws cloudformation describe-stacks --stack-name "#{StackName}" --query 'Stacks[0].Outputs')
echo "${OUTPUTS_CMD[*]}"
OUTPUTS=$("${OUTPUTS_CMD[@]}")

However, other commands require input, and while I can successfully echo these commands, I can't actually get them to run.

GET_FUNCTION_NAME_CMD=(jq --raw-output "'map(select(.OutputKey == \"ProxyFunctionName\")) | .[].OutputValue'")
echo "${GET_FUNCTION_NAME_CMD[*]} <<< \"\$OUTPUTS\""
FUNCTION_NAME=$("${GET_FUNCTION_NAME_CMD[@]}" <<< "$OUTPUTS")

The above example outputs the following, which if copied and pasted returns the correct value.

jq --raw-output 'map(select(.OutputKey == "ProxyFunctionName")) | .[].OutputValue' <<< "$OUTPUTS"

However, the command "${GET_FUNCTION_NAME_CMD[@]}" <<< "$OUTPUTS" returns an error. The same error occurs if I echo "$OUTPUTS" and pipe that in to the command saved in the variable instead of using a herestring, so I believe the error is with how the command is defined in the array.

$ FUNCTION_NAME=$("${GET_FUNCTION_NAME_CMD[@]}" <<< "$OUTPUTS")

jq: error: syntax error, unexpected INVALID_CHARACTER, expecting $end (Unix shell quoting issues?) at <top-level>, line 1:
'map(select(.OutputKey == "ProxyFunctionName")) | .[].OutputValue'
jq: 1 compile error

How can I get the command in the variable to run with a herestring?


Example value for $OUTPUTS

[
    {
        "OutputKey": "ProxyFunctionName",
        "OutputValue": "MyFunctionName",
        "Description": "Proxy Lambda Function ARN"
    },
    {
        "OutputKey": "ProxyFunctionUrl",
        "OutputValue": "https://my.function.url",
        "Description": "Proxy Lambda Function invocation URL"
    }
]
David Gard
  • 11,225
  • 36
  • 115
  • 227
  • Start with https://shellcheck.net – Shawn Apr 12 '22 at 09:56
  • use `<<< "$OUTPUTS"` instead? – Fravadona Apr 12 '22 at 09:57
  • @Fravadona - Still doesn't work, but thanks. – David Gard Apr 12 '22 at 10:00
  • Could you please add a [mre] – 0stone0 Apr 12 '22 at 10:08
  • @0stone0 - The code is already in the question. – David Gard Apr 12 '22 at 10:15
  • @Shawn - I wasn't aware of that site., so thanks for mentioning. I have checked my code and it was valid, bar a couple of recommendations which I've now included in my question. – David Gard Apr 12 '22 at 10:20
  • @DavidGard, but you need to provide at least a non-working example of "$OUTPUTS" – Fravadona Apr 12 '22 at 10:21
  • @Fravadona - `$OUTPUTS` works, but I have added an example of them for prosperity. The issue is the `jq` command in an array that is used to query those `$OUTPUTS`. – David Gard Apr 12 '22 at 10:34
  • try with `jq --raw-output 'map(select(.OutputKey == "ProxyFunctionName")) | .[].OutputValue'` – Fravadona Apr 12 '22 at 10:37
  • @Fravadona - Are you suggesting I use that as `GET_FUNCTION_NAME_CMD=(jq --raw-output 'map(select(.OutputKey == "ProxyFunctionName")) | .[].OutputValue')`? If so, that doesn't work because the single quote around the query are dropped, so the `jq` command fails. – David Gard Apr 12 '22 at 10:47
  • It doesn't fail if you use it like this `"${GET_FUNCTION_NAME_CMD[@]}" <<< "$OUTPUTS"` – Fravadona Apr 12 '22 at 10:51
  • @Fravadona - OK, that's good to know, thanks. However my issue now is that the command that is output using `echo "${GET_FUNCTION_NAME_CMD[*]} <<< \"\$OUTPUTS\""` doesn't work, as the single quotes have been dropped. My requirement unfortunately is that the command should work if copied and pasted. – David Gard Apr 12 '22 at 10:59
  • 1
    That would be `printf '%q ' "${GET_FUNCTION_NAME_CMD[@]}"; printf '<<< %q\n' "$OUTPUTS"` The copy/paste will work as expected but it's not very readable; you don't have a choice though, because you need correct escaping – Fravadona Apr 12 '22 at 11:04
  • Thanks, I think that'll get me where I need to go. I've changed slightly as I want to output `"$OUTPUTS"`, not actually the value of that variable - `printf "%q " "${GET_FUNCTION_NAME_CMD[@]}"; echo "<<< \"\$OUTPUTS\""`. I notice that every quote, space and bracket has a slash in front which makes it hard to read, but it does work with a copy/paste and that's the main thing. – David Gard Apr 12 '22 at 11:14
  • 1
    I would use `echo '<<< "$OUTPUTS"'`. BTW you can use a trick for forcing the `$'...'` notation in `printf %q`. try with `GET_FUNCTION_NAME_CMD=(jq --raw-output $'map(select(.OutputKey == "ProxyFunctionName")) | .[].OutputValue \n')` – Fravadona Apr 12 '22 at 11:27
  • @Fravadona - Yep, `echo '<<< "$OUTPUTS"'` makes sense. And thanks for the other trick. Now other than the newline at the end of the command it's escape free and much easier to read. – David Gard Apr 12 '22 at 13:55

1 Answers1

-1
OUTPUTS=$(cat <<JSON
[
    {
        "OutputKey": "ProxyFunctionName",
        "OutputValue": "MyFunctionName",
        "Description": "Proxy Lambda Function ARN"
    },
    {
        "OutputKey": "ProxyFunctionUrl",
        "OutputValue": "https://my.function.url",
        "Description": "Proxy Lambda Function invocation URL"
    }
]
JSON
)
OutputKey=ProxyFunctionName

GET_FUNCTION_NAME_CMD="jq -r '.[] | objects | select(.OutputKey == \"$OutputKey\") | .OutputValue'"
echo "$GET_FUNCTION_NAME_CMD <<<\"\$OUTPUTS\""
# jq -r '.[] | objects | select(.OutputKey == "ProxyFunctionName") | .OutputValue' <<<"$OUTPUTS"
FUNCTION_NAME=$(eval $GET_FUNCTION_NAME_CMD <<<"$OUTPUTS")
echo $FUNCTION_NAME
# MyFunctionName
SergA
  • 1,097
  • 13
  • 21
  • Thanks, but this doesn't achieve my goal. I already know the `jq` query to use, but I need to output the command that runs the query, hence the use of an array to store the command. Your answer does not take that in to account. – David Gard Apr 12 '22 at 10:45
  • I missed it, updated the answer. – SergA Apr 12 '22 at 11:00