#!/bin/bash # -*- mode:shell-script; coding:utf-8; -*- # # Created: # Last Updated: <2015-September-10 15:11:14> # ## ======================================================= ## json parsing json_throw () { echo "$*" >&2 exit 1 } BRIEF=0 LEAFONLY=1 PRUNE=0 json_awk_egrep () { local pattern_string=$1 gawk '{ while ($0) { start=match($0, pattern); token=substr($0, start, RLENGTH); print token; $0=substr($0, start+RLENGTH); } }' pattern=$pattern_string } json_tokenize () { local GREP local ESCAPE local CHAR if echo "test string" | egrep -ao --color=never "test" &>/dev/null then GREP='egrep -ao --color=never' else GREP='egrep -ao' fi if echo "test string" | egrep -o "test" &>/dev/null then ESCAPE='(\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})' CHAR='[^[:cntrl:]"\\]' else GREP=json_awk_egrep ESCAPE='(\\\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})' CHAR='[^[:cntrl:]"\\\\]' fi local STRING="\"$CHAR*($ESCAPE$CHAR*)*\"" local NUMBER='-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?' local KEYWORD='null|false|true' local SPACE='[[:space:]]+' $GREP "$STRING|$NUMBER|$KEYWORD|$SPACE|." | egrep -v "^$SPACE$" } json_parse_array () { local index=0 local ary='' read -r token case "$token" in ']') ;; *) while : do json_parse_value "$1" "$index" index=$((index+1)) ary="$ary""$value" read -r token case "$token" in ']') break ;; ',') ary="$ary," ;; *) json_throw "EXPECTED , or ] GOT ${token:-EOF}" ;; esac read -r token done ;; esac [ "$BRIEF" -eq 0 ] && value=`printf '[%s]' "$ary"` || value= : } json_parse_object () { local key local obj='' read -r token case "$token" in '}') ;; *) while : do case "$token" in '"'*'"') key=$token ;; *) json_throw "EXPECTED string GOT ${token:-EOF}" ;; esac read -r token case "$token" in ':') ;; *) json_throw "EXPECTED : GOT ${token:-EOF}" ;; esac read -r token json_parse_value "$1" "$key" obj="$obj$key:$value" read -r token case "$token" in '}') break ;; ',') obj="$obj," ;; *) json_throw "EXPECTED , or } GOT ${token:-EOF}" ;; esac read -r token done ;; esac [ "$BRIEF" -eq 0 ] && value=`printf '{%s}' "$obj"` || value= : } json_parse_value () { local jpath="${1:+$1,}$2" isleaf=0 isempty=0 print=0 case "$token" in '{') json_parse_object "$jpath" ;; '[') json_parse_array "$jpath" ;; # At this point, the only valid single-character tokens are digits. ''|[!0-9]) json_throw "EXPECTED value GOT ${token:-EOF}" ;; *) value=$token isleaf=1 [ "$value" = '""' ] && isempty=1 ;; esac [ "$value" = '' ] && return [ "$LEAFONLY" -eq 0 ] && [ "$PRUNE" -eq 0 ] && print=1 [ "$LEAFONLY" -eq 1 ] && [ "$isleaf" -eq 1 ] && [ $PRUNE -eq 0 ] && print=1 [ "$LEAFONLY" -eq 0 ] && [ "$PRUNE" -eq 1 ] && [ "$isempty" -eq 0 ] && print=1 [ "$LEAFONLY" -eq 1 ] && [ "$isleaf" -eq 1 ] && \ [ $PRUNE -eq 1 ] && [ $isempty -eq 0 ] && print=1 [ "$print" -eq 1 ] && printf "[%s]\t%s\n" "$jpath" "$value" : } json_parse () { read -r token json_parse_value read -r token case "$token" in '') ;; *) json_throw "EXPECTED EOF GOT $token" ;; esac } #usage: # cat example.json | json_tokenize | json_parse ## =======================================================