16

I'm trying to write a bash script with make s curl call and get a json document back. The json document has a key called access_token and I need to extract the value of this field. This is my json document

echo $json

{"access_token":"kjdshfsd", "key2":"value"}

I don't have jq installed.

3
  • not sure what you mean
    – HHH
    Nov 8, 2019 at 20:01
  • Related: unix.stackexchange.com/questions/551159/…
    – Jeff Schaller
    Nov 8, 2019 at 20:01
  • Also note that if this response is received via curl it's almost certain the json object will not be in the same order upon subsequent requests. (ie. A proper json parser is the only real solution)
    – jesse_b
    Nov 8, 2019 at 20:17

7 Answers 7

20

The short answer: Install jq

You shouldn't parse json without a json parser.

To do this with jq:

echo "$json" | jq -r '.access_token'

or, without letting echo expand encoded tabs and newlines,

jq -r -n --argjson data "$json" '$data.access_token'

My preferred json parser is json, using it you could do:

echo "$json" | json access_token

Note: both of these solutions assume your json object is exactly (or at least pretty much exactly) as you have shown in your example.

3
  • for some reasons I can't install jq
    – HHH
    Nov 8, 2019 at 20:09
  • Also note I have provided a non-jq solution as well.
    – jesse_b
    Nov 8, 2019 at 20:18
  • 3
    @HHH Also note that jq is available for download in a form that does not require root access for installation. There is really no reason to write a bad JSON parser yourself when you're much better off downloading jq into your home directory and using it from there.
    – Kusalananda
    Aug 31, 2020 at 16:44
19

No jq, awk, sed:

#!/bin/bash
json='{"access_token":"kjdshfsd", "key2":"value"}' 
    
echo $json | grep -o '"access_token":"[^"]*' | grep -o '[^"]*$'

Tested & working here: https://ideone.com/Fw4How

source: https://brianchildress.co/parse-json-using-grep

6
  • 1
    If anyone reading this in future and the last grep doesn't work, you can try removing the "$" and instead appending | tail -1 grep -o '[^"]*' | tail -1 Aug 26, 2022 at 15:07
  • You could use the -E/--extended-regexp argument of grep (to select the wanted key-value) and then pipe sed (to remove the key). Something like: echo $json | grep -oE '"access_token":"[^"]+' | sed -e 's/"access_token":"\(.*\)/\1/' Dec 19, 2022 at 9:34
  • 1
    Or, even better, use the -P/--perl-regexp argument of grep with look-behind and look-ahead. Something like: echo $json | grep -oP '(?<="access_token":")[^"]+(?=")' Dec 19, 2022 at 9:35
  • 1
    @G-ManSays'ReinstateMonica' If you see my second comment it can be done with a single grep (tested here: ideone.com/Z6sOBp). That said, I also like your sed-based solution. :) Dec 21, 2022 at 13:32
  • 1
    This was so minimal and straight forward, thanks
    – Reza
    Dec 25, 2022 at 15:41
2

If you can't install a json parser then assuming your strings cannot contain double quotes or newlines, every record is on a single line and that every tag and value are double-quote-enclosed strings as in your posted sample input, this will work using any awk in any shell on every UNIX box:

$ cat tst.awk
{
    while ( match($0,/"[^"]*"/) ) {
        hit = substr($0,RSTART+1,RLENGTH-2)
        if ( ++cnt % 2 ) {
            tag = hit
        }
        else {
            val = hit
            f[tag] = val
        }
        $0 = substr($0,RSTART+RLENGTH)
    }
    print f[tgt]
}

$ echo "$json" | awk -v tgt='access_token' -f tst.awk
kjdshfsd

$ echo "$json" | awk -v tgt='key2' -f tst.awk
value
1

pcregrep has a smarter -o option that allows you to request an individual capture group.  So,

echo "$json" | pcregrep -o1 '"access_token":"([^"]*)'

This is basically the same as social’s answer, except it doesn’t require as many processes.


Or, doing the (conceptually) same thing entirely in sed,

echo "$json" | sed -En 's/.*"access_token":"([^"]*).*/\1/p'

This is inspired by Paolo Rovelli’s comment.

1

As an alternative option to jq, recents version of miller (mlr) support JSON input as long as it represents tabular data like in your case of a JSON object with only scalar values which is like a row in a table.

printf %s "$json" | mlr --j2n cut -f access_token

mlr has that advantage (or disadvantage depending on PoV) of not reformatting values. For instance 1e1 or 10.00000000000000001 would not become 10.

-1

Here's another option for extracting a value.

#! /bin/sh

fileName=$1
keyName=$2

# replace all of the comments from the file with newlines.
sed -i 's|,|\n|g' $fileName

# Grab all of the text after the colon (:) on the line containing the key and save it off as a variable named value
value=$(grep $keyName $fileName | cut -d ":" -f2-)

echo $value

Please note, this process is destructive (it uses sed's in-place editing). If you care about the json file you're reading from you should make a copy of it first. Also, it does not trim off the quotation marks around the value. If you wish to remove them also, I suggest using substring.

-1

If you just want the "access_token", and you kow the format is not too difficult (no quote escapes etc.) then this will do it...

grep -oP '"access_token":"\K[^"]+'  <<< "$json"
  • The <<< "$json feeds the string into "grep"
  • The -oP options mean only the 'matching part' of the perl regex is to be returned.
  • The '\K' in the perl-RE means find the part before, but only match (return) the part after.

The result is that it finds the key, and returns the value (up until the first quote).

Fast small simple! Works for many key-value file formats, not just json!

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .