I'm going to preface by saying that "no, find a different way to do it" is an acceptable answer here.
Is there a reliable way to store a short bit of JSON in a bash variable for use in a AWS CLI command running from the same script?
I'll be running a job from Jenkins that's updating an AWS Route53 record, which requires UPSERTing a JSON file with the change in records. Because it's running from Jenkins, there's no local storage where I can keep this file, and I'd really like to avoid needing to do a git checkout every time this project will run (which will be once an hour).
Ideally, storing the data in a variable ($foo
) and calling it as part of the change-resource-record-sets
command would be most convenient given the Jenkins setup, but I'm unfamiliar with exactly how to quote/store JSON inside bash - can it be done safely?
The specific JSON in this case is the following;
{"Comment":"Update DNSName.","Changes":[{"Action":"UPSERT","ResourceRecordSet":{"Name":"alex.","Type":"A","AliasTarget":{"HostedZoneId":"######","DNSName":"$bar","EvaluateTargetHealth":false}}}]}
As an added complication the DNSName value - $bar
- needs to be expanded.
Variables provide a new way to tackle different scenarios where JSON schema alone fails. This means, that you can use a new keyword named $vars to make your life easier.
bash [filename] runs the commands saved in a file. $@ refers to all of a shell script's command-line arguments. $1 , $2 , etc., refer to the first command-line argument, the second command-line argument, etc. Place variables in quotes if the values might have spaces in them.
Unfortunately, shells such as Bash can't interpret and work with JSON directly. This means that working with JSON via the command line can be cumbersome, involving text manipulation using a combination of tools such as sed and grep.
You could use a here-doc:
foo=$(cat <<EOF {"Comment":"Update DNSName.","Changes":[{"Action":"UPSERT","ResourceRecordSet":{"Name":"alex.","Type":"A","AliasTarget":{"HostedZoneId":"######","DNSName":"$bar","EvaluateTargetHealth":false}}}]} EOF )
By leaving EOF
in the first line unquoted, the contents of the here-doc will be subject to parameter expansion, so your $bar
expands to whatever you put in there.
If you can have linebreaks in your JSON, you can make it a little more readable:
foo=$(cat <<EOF { "Comment": "Update DNSName.", "Changes": [ { "Action": "UPSERT", "ResourceRecordSet": { "Name": "alex.", "Type": "A", "AliasTarget": { "HostedZoneId": "######", "DNSName": "$bar", "EvaluateTargetHealth": false } } } ] } EOF )
or even (first indent on each line must be a tab)
foo=$(cat <<-EOF { "Comment": "Update DNSName.", "Changes": [ { "Action": "UPSERT", "ResourceRecordSet": { "Name": "alex.", "Type": "A", "AliasTarget": { "HostedZoneId": "######", "DNSName": "$bar", "EvaluateTargetHealth": false } } } ] } EOF )
and to show how that is stored, including quoting (assuming that bar=baz
):
$ declare -p foo declare -- foo="{ \"Comment\": \"Update DNSName.\", \"Changes\": [ { \"Action\": \"UPSERT\", \"ResourceRecordSet\": { \"Name\": \"alex.\", \"Type\": \"A\", \"AliasTarget\": { \"HostedZoneId\": \"######\", \"DNSName\": \"baz\", \"EvaluateTargetHealth\": false } } } ] }"
Because this expands some shell metacharacters, you could run into trouble if your JSON contains something like `
, so alternatively, you could assign directly, but be careful about quoting around $bar
:
foo='{"Comment":"Update DNSName.","Changes":[{"Action":"UPSERT","ResourceRecordSet":{"Name":"alex.","Type":"A","AliasTarget":{"HostedZoneId":"######","DNSName":"'"$bar"'","EvaluateTargetHealth":false}}}]}'
Notice the quoting for $bar
: it's
"'"$bar"'" │││ │││ │││ ││└ literal double quote │││ │└ opening syntactical single quote │││ └ closing syntactical double quote ││└ opening syntactical double quote │└ closing syntactical single quote └ literal double quote
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With