1

I'm trying to create a bash script that automates configuration of some letsencrypt related stuff.

The file that I have to edit is json so I would just use jq to edit it and pass the site name to it from the positional arguments of the script, but I can't get the positional argument passed into the json text.

I'm trying to do domething like the following:

JSON=`jq '. + { "ssl_certificate": "/etc/letsencrypt/live/$2/fullchain.pem" }' <<< echo site_config.json`
JSON=`jq '. + { "ssl_certificate_key": "/etc/letsencrypt/live/$2/fullchain.pem" }' <<< ${JSON}`
echo -e "$JSON" > site_config.json

Where the second positional argument ($2) contain the domain name required to be set in the json file.

How this can be done?

Original json:

{
  "key1":"value1",
  "key2":"value2"
}

Wanted json:

{
  "key1":"value1",
  "key2":"value2",
  "ssl_certificate": "/etc/letsencrypt/live/somesite.com/fullchain.pem",
  "ssl_certificate_key": "/etc/letsencrypt/live/somesite.com/fullchain.pem"
}
F. Hauri - Give Up GitHub
  • 64,122
  • 17
  • 116
  • 137
The amateur programmer
  • 1,238
  • 3
  • 18
  • 38

2 Answers2

1

1. String construction under

I use printf and octal representation for nesting quotes and double quotes:

printf -v JSON 'Some "double quoted: \047%s\047"' "Any string"
echo "$JSON"
Some "double quoted: 'Any string'"

2. Using jq, strictly answer to edited question:

myFunc() {
    local file="$1" site="$2" JSON
    printf -v JSON '. + {
        "ssl_certificate": "/etc/letsencrypt/live/%s/fullchain.pem",
        "ssl_certificate_key": "/etc/letsencrypt/live/%s/fullchain.pem"
    }' "$site" "$site"
    jq "$JSON" <"$file"
}

Then run:

myFunc site_config.json test.com
{
  "key1": "value1",
  "key2": "value2",
  "ssl_certificate": "/etc/letsencrypt/live/test.com/fullchain.pem",
  "ssl_certificate_key": "/etc/letsencrypt/live/test.com/fullchain.pem"
}

myFunc site_config.json test.com >site_config.temp && mv site_config.{temp,conf}

Or even:

myFunc <(
    printf '{ "key1":"value1","key2":"value2","comment":"Let\047s doit\041"  }'
    ) test.com

Will render:

{
  "key1": "value1",
  "key2": "value2",
  "comment": "Let's doit!",
  "ssl_certificate": "/etc/letsencrypt/live/test.com/fullchain.pem",
  "ssl_certificate_key": "/etc/letsencrypt/live/test.com/fullchain.pem"
}

2b. Better written with jq's --arg option:

Thanks to peak's detailed answer!

I use arrays to store strings with quotes, spaces and other special characters. This is more readable as there is no need to escape end-of-line (backslashes) and permit comments:

myFunc() {
    local file="$1" site="$2"
    local JSON=(
        --arg ssl_certificate "/etc/letsencrypt/live/$site/fullchain.pem"
        --arg ssl_certificate_key "/etc/letsencrypt/live/$site/fullchain.pem"
        '. + {$ssl_certificate, $ssl_certificate_key}' # this syntax
        # do offer two advantages: 1: no backslashes and 2: permit comments.
    )
    jq "${JSON[@]}" <"$file"
}

3. Inline edit function

For editing a small script. I prefer to use cp -a in order to preserve attributes and ensure a valid operation before replacement.

If you plan to use this, mostly for replacing, you could add replacement in your function:

myFunc() {
    local REPLACE=false
    [ "$1" = "-r" ] && REPLACE=true && shift
    local file="$1" site="$2"
    local JSON=( --arg ssl_certificate "/etc/letsencrypt/live/$site/fullchain.pem"
        --arg ssl_certificate_key "/etc/letsencrypt/live/$site/fullchain.pem"
        '. + {$ssl_certificate, $ssl_certificate_key}' )
    if $REPLACE;then
        cp -a "$file" "${file}.temp"
        exec {out}>"${file}.temp"
    else
        exec {out}>&1
    fi
    jq "${JSON[@]}" <"$file" >&$out &&
        $REPLACE && mv "${file}.temp" "$file"
    exec {out}>&-
}

Then to modify file instead of dumping result to terminal, you have to add -r option:

myFunc -r site_config.json test.org
F. Hauri - Give Up GitHub
  • 64,122
  • 17
  • 116
  • 137
1

I cannot get the positional argument passed into the json text.

In general, by far the best way to do that is to use jq's --arg and/or --argjson command-line options. This is safe, and in the present case means that you would only have to call jq once. E.g.:

< site_config.json \
jq --arg sslc "/etc/letsencrypt/live/$2/fullchain.pem" \
   --arg sslck "/etc/letsencrypt/live/$2/fullchain.pem" '
   . + {ssl_certificate: $sslc, ssl_certificate_key: $sslck }'

Once you're sure things are working properly, feel free to use moreutils's sponge :-)

A DRY-er solution

Thanks to a neat convenience feature of jq, one can write more DRY-ly:

< site_config.json \
jq --arg ssl_certificate "/etc/letsencrypt/live/$2/fullchain.pem" \
   --arg ssl_certificate_key "/etc/letsencrypt/live/$2/fullchain.pem" '
   . + {$ssl_certificate, $ssl_certificate_key }'
``


peak
  • 105,803
  • 17
  • 152
  • 177
  • I don't use morutils. for this, I often use: `file=site_config.json;jq "${JSON[@]}" < $file | diff -u $file - | tee >(patch -p0) | grep ^+` . This modify file and show new lines. – F. Hauri - Give Up GitHub Oct 28 '20 at 09:11