Search code examples
jsonbashfunctionechoquoting

bash function input $1 is only processing first word of string & formatting issue


My BASH function:

json_format () {
        echo '{
                "question": "';echo "$1";echo '",' 
}

for:

json_format ${questions[$Q_counter]}

Is returning:

{
                "question": "
VM
",

Instead of expected json format and string:

{
    "question": "VM CPU is at 100% and you'r jumpbox creds be broken! What do you do?",

The string appears to be cut off at the space after the first word "VM", and the formatting is a little off with those echo commands. How can i correct my function? Thanks!


Solution

  • The ideal way to generate JSON from shell is to use a tool such as jq that actually understands the format:

    json_format() {
      jq -n --arg q "$1" '{"question": $q}'
    }
    

    ...or, if you have a Python interpreter, the built-in json module can also be used:

    json_format() {
      python -c 'import sys, json; print json.dumps({"question": sys.argv[1]})' "$1"
    }
    

    If you don't have any of those tools, however, at least make a best-effort attempt at escaping:

    json_format() {
      local s=$1
      s=${s//'\'/'\\'}   # replace \ with \\
      s=${s//'"'/'\"'}   # replace " with \"
      s=${s//$'\n'/'\n'} # replace newline literals with \n
      printf '{\n\t"question": "%s"\n}\n' "$s"
    }
    

    ...or, to process one value at a time:

    json_format() {
      local s=$1
      s=${s//'\'/'\\'}   # replace \ with \\
      s=${s//'"'/'\"'}   # replace " with \"
      s=${s//$'\n'/'\n'} # replace newline literals with \n
      printf '%s\n' "$s"
    }
    

    ...invoked individually for each string to be formatted, as in:

    cat <<EOF
    {
      "question": "$(json_format "$question")",
      "image": "$(json_format "$image_url")",
      "choices": [ ],
      "correct": [ "$(json_format "$answer")" ],
      "explanation": "$(json_format "$explanation")"
    }
    EOF
    

    This will work correctly with cases for which naive approaches will produce valid that is not in fact valid JSON. Consider, for instance:

    # naive string substitution will fail here because it won't escape double quotes
    json_format 'How do you spell "hello" in Spanish?'
    

    or

    # naive string substitution will fail here because it won't escape the newline
    json_format $'This question has\ntwo lines'
    

    or

    # naive string substitution will fail here because it won't escape the backslash
    json_format 'This question ends in a backslash: \'
    

    Note, in all of the above, the quoting -- which ensures that the string is passed as a single argument.