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.

jesse_b
  • 37,005
HHH
  • 287
  • not sure what you mean – HHH Nov 08 '19 at 20:01
  • Related: https://unix.stackexchange.com/questions/551159/parse-value-from-different-json-strings-no-jq – Jeff Schaller Nov 08 '19 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 08 '19 at 20:17

7 Answers7

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.

jesse_b
  • 37,005
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

social
  • 299
  • 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 – Dmytro Lysak Aug 26 '22 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/' – Paolo Rovelli Dec 19 '22 at 09: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":")[^"]+(?=")' – Paolo Rovelli Dec 19 '22 at 09:35
  • @PaoloRovelli: (1) Piping one grep into another grep is somewhat unaesthetic, but it’s sometimes a good approach.  Piping grep into sed is almost always overkill; sed is powerful enough that you almost never need to combine it with grep.  See my answer for how to do this using sed and not grep. (2) If you have a new, different answer, I suggest that you post it as an answer. – G-Man Says 'Reinstate Monica' Dec 20 '22 at 02:27
  • 1
    @G-ManSays'ReinstateMonica' If you see my second comment it can be done with a single grep (tested here: https://ideone.com/Z6sOBp). That said, I also like your sed-based solution. :) – Paolo Rovelli Dec 21 '22 at 13:32
  • 1
    This was so minimal and straight forward, thanks – Reza Dec 25 '22 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
Ed Morton
  • 31,617
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.

Benjamin
  • 109
-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!

anthony
  • 610