skip to Main Content

I am trying to submit a POST request using Bash that includes JSON data with a variable equal to that of a random string. I get the string dynamically by parsing a file which can vary in content… and may contain regex characters and also new lines.

I will provide an example string here with a curl request that works successfully with the API I am posting this request to. I can only get the request to go through with the string hardcoded into the JSON data and not while assigning a variable to the string like for instance stringVar and using the variable in the JSON data. I could sure use some help where I am messing this up

my working shell script looks something like this

#!/bin/bash

curl -i -H "Authorization: Bearer <MY_API_TOKEN>" -H "Content-Type: application/json" -H "Accept: application/json" -X POST 'https://api.example.com/v1/endpoint' -d '{
    "name": "my-project",
    "files": [
        {
            "data": "helloWorldFunction() {n echo "hello world" n}"
        },
    ],
}'

This works, however I need to change data’s value from the string
helloWorldFunction() {n echo "hello world" n}
to a variable

I have tried settings the variable in different ways in the JSON content from reading suggestions on other questions. For instance I have tried tried changing my shell script to

#!/bin/bash

stringVar="helloWorldFunction() {n echo "hello world" n}"

curl -i -H "Authorization: Bearer <MY_API_TOKEN>" -H "Content-Type: application/json" -H "Accept: application/json" -X POST 'https://api.example.com/v1/endpoint' -d '{
    "name": "my-project",
    "files": [
        {
            "data": "${stringVar}"
        },
    ],
}'

i have tried setting the variable like this

"${stringVar}" | turns into ${stringVar}

"$stringVar" | turns into "$stringVar"

and

"’${stringVar}’"

"’$stringVar’"

both seem to return this error
{"error":{"code":"bad_request","message":"Invalid JSON"}}curl: (3) unmatched brace in URL position 1: {n

and

stringVar

stringVar

$stringVar

"’"$stringVar"’"

""$stringVar""

${stringVar}

all seem to return this error
{"error":{"code":"bad_request","message":"Invalid JSON"}}

Ahh any help on what I am doing wrong would be great.
Thanks in advance y’all

4

Answers


  1. Like this:

    #!/bin/bash
    
    stringVar="..."
    
    curl -i -H "Authorization: Bearer <MY_API_TOKEN>" 
        -H "Content-Type: application/json" 
        -H "Accept: application/json" -X POST 'https://api.example.com/v1/endpoint' 
        -d@/dev/stdin <<EOF
    {
        "name": "my-project",
        "files": [
            {
                "data": $stringVar
            },
        ],
    }
    EOF
    

    You then should take care about what you fed in the variable, this have to be valid JSON

    Login or Signup to reply.
  2. In order to interpolate the value of a variable, you need to enclose the string in double-quotes("), but the JSON also requires literal double-quotes.

    The easiest solution is probably to use a here-document to feed the data into curl‘s standard input, as in @Gilles Quénot’s answer. But you can still pass it in via the command line; you just have to be careful with the quotes.

    This is one way:

    curl ... -d '{
      "name": "my-project",
      "files": [
        {
            "data": "'"$stringVar"'"
        }
      ]
    }'
    

    The JSON here is mostly contained within single quotation marks (''). But right after opening the pair of literal "s that will enclose the value of data, we close the single quotes and switch our shell quotation mode to double quotes in order to include the value of $stringVar. After closing the double quotes around that expansion, we go back into single quotes for the rest of the JSON, starting with closing the literal double-quotes around the value of data.

    In a language that used + for string concatenation, it would look like '... "data": "' + "$stringVar" + '"... ', but in the shell you just put the strings next to each other with no operator to concatenate them.

    As an alternative, you could put the whole thing in double-quotes, but then you need backslashes to include the literal double quotes that are part of the JSON:

    curl ... -d "{
      "name": "my-project",
      "files": [
        {
          "data": "$stringVar"
        }
      ]
    }"
    

    So that requires a lot more changes if you’re starting from plain JSON; it also looks messier, IMO.

    You can also use a tool that knows how to build JSON and let it worry about quoting etc. Here’s one way to build it with jq:

    jq -n --arg data "$stringVar" '{ 
      "name": "my-project", 
      "files": [ 
        { 
          "data": $data 
        } 
      ] 
    }'
    

    Using --arg creates a variable inside jq – I named it data – which can then be included in an expression with the syntax $varname ($data in this case). Despite the similarity of syntax, that’s not a shell interpolation; we’re passing the literal text $data to jq, and jq itself is what replaces it with the value of the variable (which was passed as the second argument to --arg).

    There’s another tool called jo, which doesn’t manipulate JSON but rather produces it, from input that is easier to generate in the shell. Here’s one way to construct the desired object with it:

    jo name=my-project files="$(jo -a "$(jo data="$stringVar")")"
    

    Either way you can include the constructed JSON in your curl command line like this:

    curl ... -d "$(jo or jq command goes here)"
    
    Login or Signup to reply.
  3. Do not generate such JSON by hand. Use a tool like jq to do it for you.

    #!/bin/bash
    
    stringVar="helloWorldFunction() {n echo "hello world" n}"
    
    jq -n --arg s "$stringVar" '{name: "my-project", files: [{data: $s}]}' |
      curl -i -H "Authorization: Bearer <MY_API_TOKEN>" 
              -H "Content-Type: application/json"  
              -H "Accept: application/json" 
              -X POST  
              -d @- 
              'https://api.example.com/v1/endpoint'
    
    Login or Signup to reply.
  4. As an alternative to jq and curl you could use to generate the JSON and submit the POST-request.

    With command-line options:

    #!/bin/bash
    
    stringVar='helloWorldFunction() {n echo "hello world" n}'
    
    xidel -s --variable var="$stringVar" 
      -H "Authorization: Bearer <MY_API_TOKEN>" 
      -H "Content-Type: application/json" 
      -H "Accept: application/json"
      -d '{serialize(
        {"name":"my-project","files":array{{"data":$var}}},
        {"method":"json"}
      )}' 
      "https://api.example.com/v1/endpoint" 
      -e '$raw'
    

    Or with the x:request() function in-query:

    #!/bin/bash
    
    stringVar='helloWorldFunction() {n echo "hello world" n}'
    
    xidel -s --variable var="$stringVar" -e '
      x:request({
        "headers":(
          "Authorization: Bearer <MY_API_TOKEN>",
          "Content-Type: application/json",
          "Accept: application/json"
        ),
        "post":serialize(
          {"name":"my-project","files":array{{"data":$var}}},
          {"method":"json"}
        ),
        "url":"https://api.example.com/v1/endpoint"
      })/raw
    '
    

    $raw / /raw returns the raw output, like curl. If the API-endpoint returns JSON, then you can use $json / /json to parse it.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search