BASH   10

JSON.sh

Guest on 13th May 2022 01:39:58 AM

  1. #!/bin/sh
  2.  
  3. throw() {
  4.   echo "$*" >&2
  5.   exit 1
  6. }
  7.  
  8. BRIEF=0
  9. LEAFONLY=0
  10. PRUNE=0
  11. NO_HEAD=0
  12. NORMALIZE_SOLIDUS=0
  13.  
  14. usage() {
  15.   echo
  16.   echo "Usage: JSON.sh [-b] [-l] [-p] [-s] [-h]"
  17.   echo
  18.   echo "-p - Prune empty. Exclude fields with empty values."
  19.   echo "-l - Leaf only. Only show leaf nodes, which stops data duplication."
  20.   echo "-b - Brief. Combines 'Leaf only' and 'Prune empty' options."
  21.   echo "-n - No-head. Do not show nodes that have no path (lines that start with [])."
  22.   echo "-s - Remove escaping of the solidus symbol (straight slash)."
  23.   echo "-h - This help text."
  24.   echo
  25. }
  26.  
  27. parse_options() {
  28.   set -- "$@"
  29.   local ARGN=$#
  30.   while [ "$ARGN" -ne 0 ]
  31.   do
  32.     case $1 in
  33.       -h) usage
  34.           exit 0
  35.       ;;
  36.       -b) BRIEF=1
  37.           LEAFONLY=1
  38.           PRUNE=1
  39.       ;;
  40.       -l) LEAFONLY=1
  41.       ;;
  42.       -p) PRUNE=1
  43.       ;;
  44.       -n) NO_HEAD=1
  45.       ;;
  46.       -s) NORMALIZE_SOLIDUS=1
  47.       ;;
  48.       ?*) echo "ERROR: Unknown option."
  49.           usage
  50.           exit 0
  51.       ;;
  52.     esac
  53.     shift 1
  54.     ARGN=$((ARGN-1))
  55.   done
  56. }
  57.  
  58. awk_egrep () {
  59.   local pattern_string=$1
  60.  
  61.   gawk '{
  62.    while ($0) {
  63.      start=match($0, pattern);
  64.      token=substr($0, start, RLENGTH);
  65.      print token;
  66.      $0=substr($0, start+RLENGTH);
  67.    }
  68.  }' pattern="$pattern_string"
  69. }
  70.  
  71. tokenize () {
  72.   local GREP
  73.   local ESCAPE
  74.   local CHAR
  75.  
  76.   if echo "test string" | egrep -ao --color=never "test" >/dev/null 2>&1
  77.   then
  78.     GREP='egrep -ao --color=never'
  79.   else
  80.     GREP='egrep -ao'
  81.   fi
  82.  
  83.   if echo "test string" | egrep -o "test" >/dev/null 2>&1
  84.   then
  85.     ESCAPE='(\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})'
  86.     CHAR='[^[:cntrl:]"\\]'
  87.   else
  88.     GREP=awk_egrep
  89.     ESCAPE='(\\\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})'
  90.     CHAR='[^[:cntrl:]"\\\\]'
  91.   fi
  92.  
  93.   local STRING="\"$CHAR*($ESCAPE$CHAR*)*\""
  94.   local NUMBER='-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?'
  95.   local KEYWORD='null|false|true'
  96.   local SPACE='[[:space:]]+'
  97.  
  98.   # Force zsh to expand $A into multiple words
  99.   local is_wordsplit_disabled=$(unsetopt 2>/dev/null | grep -c '^shwordsplit$')
  100.   if [ $is_wordsplit_disabled != 0 ]; then setopt shwordsplit; fi
  101.   $GREP "$STRING|$NUMBER|$KEYWORD|$SPACE|." | egrep -v "^$SPACE$"
  102.   if [ $is_wordsplit_disabled != 0 ]; then unsetopt shwordsplit; fi
  103. }
  104.  
  105. parse_array () {
  106.   local index=0
  107.   local ary=''
  108.   read -r token
  109.   case "$token" in
  110.     ']') ;;
  111.     *)
  112.       while :
  113.       do
  114.         parse_value "$1" "$index"
  115.         index=$((index+1))
  116.         ary="$ary""$value"
  117.         read -r token
  118.         case "$token" in
  119.           ']') break ;;
  120.           ',') ary="$ary," ;;
  121.           *) throw "EXPECTED , or ] GOT ${token:-EOF}" ;;
  122.         esac
  123.         read -r token
  124.       done
  125.       ;;
  126.   esac
  127.   [ "$BRIEF" -eq 0 ] && value=$(printf '[%s]' "$ary") || value=
  128.   :
  129. }
  130.  
  131. parse_object () {
  132.   local key
  133.   local obj=''
  134.   read -r token
  135.   case "$token" in
  136.     '}') ;;
  137.     *)
  138.       while :
  139.       do
  140.         case "$token" in
  141.           '"'*'"') key=$token ;;
  142.           *) throw "EXPECTED string GOT ${token:-EOF}" ;;
  143.         esac
  144.         read -r token
  145.         case "$token" in
  146.           ':') ;;
  147.           *) throw "EXPECTED : GOT ${token:-EOF}" ;;
  148.         esac
  149.         read -r token
  150.         parse_value "$1" "$key"
  151.         obj="$obj$key:$value"        
  152.         read -r token
  153.         case "$token" in
  154.           '}') break ;;
  155.           ',') obj="$obj," ;;
  156.           *) throw "EXPECTED , or } GOT ${token:-EOF}" ;;
  157.         esac
  158.         read -r token
  159.       done
  160.     ;;
  161.   esac
  162.   [ "$BRIEF" -eq 0 ] && value=$(printf '{%s}' "$obj") || value=
  163.   :
  164. }
  165.  
  166. parse_value () {
  167.   local jpath="${1:+$1,}$2" isleaf=0 isempty=0 print=0
  168.   case "$token" in
  169.     '{') parse_object "$jpath" ;;
  170.     '[') parse_array  "$jpath" ;;
  171.     # At this point, the only valid single-character tokens are digits.
  172.     ''|[!0-9]) throw "EXPECTED value GOT ${token:-EOF}" ;;
  173.     *) value=$token
  174.        # if asked, replace solidus ("\/") in json strings with normalized value: "/"
  175.        [ "$NORMALIZE_SOLIDUS" -eq 1 ] && value=$(echo "$value" | sed 's#\\/#/#g')
  176.        isleaf=1
  177.        [ "$value" = '""' ] && isempty=1
  178.        ;;
  179.   esac
  180.   [ "$value" = '' ] && return
  181.   [ "$NO_HEAD" -eq 1 ] && [ -z "$jpath" ] && return
  182.  
  183.   [ "$LEAFONLY" -eq 0 ] && [ "$PRUNE" -eq 0 ] && print=1
  184.   [ "$LEAFONLY" -eq 1 ] && [ "$isleaf" -eq 1 ] && [ $PRUNE -eq 0 ] && print=1
  185.   [ "$LEAFONLY" -eq 0 ] && [ "$PRUNE" -eq 1 ] && [ "$isempty" -eq 0 ] && print=1
  186.   [ "$LEAFONLY" -eq 1 ] && [ "$isleaf" -eq 1 ] && \
  187.     [ $PRUNE -eq 1 ] && [ $isempty -eq 0 ] && print=1
  188.   [ "$print" -eq 1 ] && printf "[%s]\t%s\n" "$jpath" "$value"
  189.   :
  190. }
  191.  
  192. parse () {
  193.   read -r token
  194.   parse_value
  195.   read -r token
  196.   case "$token" in
  197.     '') ;;
  198.     *) throw "EXPECTED EOF GOT $token" ;;
  199.   esac
  200. }
  201.  
  202. if ([ "$0" = "$BASH_SOURCE" ] || ! [ -n "$BASH_SOURCE" ]);
  203. then
  204.   parse_options "$@"
  205.   tokenize | parse
  206. fi
  207.  
  208. # vi: expandtab sw=2 ts=2

Raw Paste


Login or Register to edit or fork this paste. It's free.