#!/bin/bash # # Author: Georg Voell - georg.voell@standby.cloud # Version: @(#)browse-json 3.2.1 14.10.2024 (c)2024 Standby.cloud # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ # # This script can be used free of charge. Use it as is or customize as needed. It is not guaranteed to be # error free and you will not be reimbursed for any damage it may cause. # #@ Browse through JSON items and print all or single values. #@ #@Usage: browse-json [options] [key] #@ Options: #@ -h, --help : Displays helptext. #@ -v, --version : Displays the version of the script. #@ -q, --quiet : Just write error message to LOGFILE. #@ -r, --raw : Don't convert hyphens e.g. lifecycle-state to lifecycleState #@ -o, --output : Output format: can be "json", "keys" or "plain" (default). #@ -i, --import : Read from a file ( = filename) - if not specified, read from stdin. #@ -s, --select : Select only the nth JSON item (starting with 1 for the first record). #@ Key: This script can be called with an optional parameter "key" (otherwise it prints out all keys). #@ #@Examples: #@ #@ get-platform --output json | browse-json --select 1 --raw creator #@ get-platform --output json | browse-json --select 1 --raw items --output json #@ get-platform --output json | browse-json --select 1 --raw content/processor #@ get-platform --output json | browse-json --select 1 os #@ get-platform --output json | browse-json [0]/os # # More advanced examples: # get-platform --output json | browse-json --raw --output json # Displays the the input you can search for # get-platform --output json | browse-json --raw '[0]/content/os' # curl -skL http://169.254.169.254/opc/v1/instance | browse-json [0]/agentConfig/pluginsConfig[0] --output json # # Exit codes: # 01: Unknown or wrong parameter. # 02: No **jq** or wrong version of **jq**. This script needs **jq** to format "json". # 03: Error in JSON file or no input. # 04: Key not found. # 99: User interrupt. # # Update history: # # V 3.0.0 29.04.2020 New version # V 3.0.1 11.06.2020 Using library # V 3.0.2 11.07.2020 Fixed an error with dashes in keyname # V 3.0.3 11.01.2021 Using script "norm-json" # V 3.0.4 26.03.2021 New parameter --quiet # V 3.0.5 19.04.2021 Fixed bug with 'lparam' # V 3.1.0 05.06.2023 New copyright # V 3.2.0 17.08.2024 New minor version: Options --import and keys # V 3.2.1 14.10.2024 Output format: json, keys and plain # # Find executable bash library and source it lib=`which lib.bash 2>/dev/null | sed 's|^no 'lib.bash' in .*||'` if [ "$lib" != "" ]; then source "$lib" else progdir=`dirname "$0"` if [ -r "${progdir}/lib.bash" ]; then source "${progdir}/lib.bash" else echo "Unexpected error: Unable to locate bash library 'lib.bash'." exit 1 fi fi # Do extra cleanup function ExtraCleanup() { filecheck -rm ${scratchfile}.keys filecheck -rm ${scratchfile}.out } # Presets qopt="" ropt="" nopt="" recursive="" param="" number="" filename="" formatstr="" # Loop until all parameters are used up while [ $# -gt 0 ]; do pname=${1} case "$pname" in -i | --import) shift if [ "$1" != "" ]; then if [ "$filename" = "" ]; then filename="$1" if [ ! -r "$filename" ]; then errstr="Can't read from filename '$filename'." fi else errstr="Option '$pname' used more then once." fi shift else errstr="Please specify a filename after parameter '$pname'." fi ;; -o | --output) shift if [ "$1" != "" ]; then if [ "$formatstr" = "" ]; then formatstr=`echo "$1" | tolower` if [ "$formatstr" != "json" -a "$formatstr" != "keys" -a "$formatstr" != "plain" ]; then errstr="Unknown format '$formatstr' after parameter '$pname'. Please choose from 'json', 'keys' or 'plain'." fi else errstr="Option '$pname' used more then once." fi shift else errstr="Please specify a format ('json', 'keys' or 'plain') after parameter '$pname'." fi ;; --recursive) shift if [ "$1" != "" ]; then recursive="$1" shift else errstr="Please specify a searchbase (e.g. .attributes) after parameter '$pname'." fi ;; -s | --select) shift if [ "$1" != "" ]; then if [ "$number" = "" ]; then number="$1" range=`echo "$number" | grep '^[0123456789]*$'` if [ "$range" = "" ]; then errstr="Invalid number after option '$pname'." else nopt="$pname $number" fi else errstr="Option '$pname' used more then once." fi shift else errstr="Please specify a number after option '$pname'." fi ;; -r | --raw) shift ropt="$pname" ;; -q | --quiet) shift qopt="$pname" ;; -v | --version) shift showversion=true ;; -h | --help) shift showhelp=true ;; *) shift paramck=`echo "$pname" | grep '^-'` # Keys don't begin with '-' if [ "$paramck" != "" ]; then errstr="Unknown option '$pname'." else if [ "$errstr" = "" ]; then if [ "$param" = "" ]; then param=`echo "$pname" | tr -d ' ' | sed 's|^/*||' | tr -s '/'` # | sed 's|/$||'` # Don't lowercase because OCI supports Mixed cases else errstr="Key was already specified '$param'. Unknown additional parameter: '$pname'." fi fi fi esac done # Display help or error message DisplayHelp # Set defaults #if [ "$nopt" = "" ]; then # # Prefere the first record in array if it is not being selected individually # nopt="--select 1" #fi # Set output to plain if not specified if [ "$formatstr" = "" ]; then formatstr="plain" fi if [ "$recursive" != "" ]; then lparam="" rparam="" sbase="$recursive" else arr="" # Check if parameter contains an '/' grepres=`echo "$param" | grep "/"` if [ "$grepres" != "" ]; then lparam=`dirname "$param"` rparam=`basename "$param"` lparam=`echo "/${lparam}/" | sed 's|\(\[[0-9]*\]\)*/|\"\1\.\"|g'` sbase=`echo "$lparam" | sed 's|^"||' | sed 's|\."$||' | sed 's|""||'` else lparam="" grepres=`echo "$param" | grep '^\['` if [ "$grepres" = "" ]; then rparam="$param" sbase="." else rparam="" sbase=".$param" fi fi grepres=`echo "$rparam" | grep '\['` if [ "$grepres" != "" ]; then arr=`echo "$rparam" | cut -d'[' -f2-` arr="[$arr" rparam=`echo "$rparam" | cut -d'[' -f1` fi fi if [ "$DEBUG_BROWSE_JSON" = true ]; then ShowVariable "script" "$script" ShowVariable "nopt" "$nopt" ShowVariable "param" "$param" ShowVariable "lparam" "$lparam" ShowVariable "rparam" "$rparam" ShowVariable "arr" "$arr" ShowVariable "sbase" "$sbase" ShowVariable "recursive" "$recursive" ShowVariable "formatstr" "$formatstr" fi # Check if he have jq in path and right version jqversok=`check-version jq --min 1.5 | cut -d' ' -f3` if [ "$jqversok" != "ok" ]; then # At least jq version 1.5 is needed for 'first(inputs)', 'keys_unsorted' and 'nth(0)' exitcode=2 errormsg $qopt $exitcode "($progstr) No 'jq' in '$PATH' or version is less than '1.5'." ClearSTDIN exit $exitcode else # Read from stdin or file if [ "$recursive" != "" ]; then cat $filename > $scratchfile 2>&1 stat=$? else if [ "$filename" != "" ]; then # Select only the first object (if there are more in input) / -n option required ### cat $filename | convert-json --output "json" | jq -n 'first(inputs)' > $scratchfile 2>&1 norm-json --import "$filename" $nopt $qopt $ropt > $scratchfile 2>&1 stat=$? else if [ ! -t 0 ]; then ### cat /dev/stdin | convert-json --output "json" | jq -n 'first(inputs)' > $scratchfile 2>&1 cat /dev/stdin | norm-json $nopt $qopt $ropt > $scratchfile 2>&1 stat=$? else exitcode=3 errormsg $qopt $exitcode "($progstr) No filename specified and stdin seems to be empty." exit $exitcode fi fi fi if [ $stat -gt 0 ]; then exitcode=3 if [ "$qopt" = "" ]; then errrsn=`head -n 2 $scratchfile` printf "${errrsn}\n" ### Don't change this line fi Cleanup exit $exitcode else if [ "$formatstr" = "json" -a "$rparam" = "" ]; then # Print the whole JSON cat $scratchfile else # Just print the keys # First create a file with key and type (e.g. string, object) arrays cat $scratchfile | jq ${sbase}' | keys_unsorted, map(type), map(.)' > ${scratchfile}.keys 2>&1 stat=$? if [ $stat -gt 0 ]; then if [ "$DEBUG_BROWSE_JSON" = true ]; then ShowVariable "Print Keys - stat" "$stat" fi exitcode=4 errormsg $qopt $exitcode "($progstr) Key '$param' not found in JSON input." Cleanup exit $exitcode fi # Now print the keys and if the key is an array - mark it with a / after the name elkey="" eltype="" elvalue="" i=0 printf "" > ${scratchfile}.out while [ "$elkey" != "null" ]; do element="`cat ${scratchfile}.keys | jq -M 'nth('$i')' | tr -d '"' | tr '\n' '\t'`" elkey=`echo "$element" | cut -d$'\t' -f1` eltype=`echo "$element" | cut -d$'\t' -f2` elvalue=`echo "$element" | cut -d$'\t' -f3` if [ "$rparam" = "" -o "$elkey" = "$rparam" ]; then if [ "$elkey" != "null" ]; then if [ "$eltype" = "array" ]; then if [ "$rparam" != "" ]; then # Recursion if [ "$recursive" = "" ]; then if [ "$sbase" != "." ]; then if [ "$rparam" = "" ]; then recursive=${sbase}'.'$arr else recursive=${sbase}'."'$rparam'"'$arr fi else if [ "$rparam" = "" ]; then recursive='.'$arr else recursive='."'$rparam'"'$arr fi fi if [ "$formatstr" = "json" ]; then printf '{\n "%s": ' $rparam > ${scratchfile}.out cat $scratchfile | jq "$recursive" >> ${scratchfile}.out printf '}\n' >> ${scratchfile}.out cat ${scratchfile}.out | jq -M . else if [ "$DEBUG_BROWSE_JSON" = true ]; then echo "Recursive Call" >/dev/stderr ShowVariable "recursive" "$recursive" fi "$script" --import $scratchfile --output "$formatstr" --recursive "$recursive" "$param" fi Cleanup exit fi else if [ "$formatstr" = "keys" ]; then echo "${elkey}:[]" >> ${scratchfile}.out else if [ "$formatstr" = "plain" ]; then if [ "$elkey" = "$rparam" ]; then echo "${elvalue}" >> ${scratchfile}.out else echo "${elkey}" >> ${scratchfile}.out fi fi fi fi else if [ "$eltype" = "object" ]; then if [ "$rparam" != "" ]; then # Recursion if [ "$recursive" = "" ]; then if [ "$sbase" != "." ]; then if [ "$rparam" = "" ]; then recursive=${sbase}'.'$arr else recursive=${sbase}'."'$rparam'"'$arr fi else if [ "$rparam" = "" ]; then recursive='.'$arr else recursive='."'$rparam'"'$arr fi fi if [ "$formatstr" = "json" ]; then printf '{\n "%s": ' $rparam > ${scratchfile}.out cat $scratchfile | jq "$recursive" >> ${scratchfile}.out printf '}\n' >> ${scratchfile}.out cat ${scratchfile}.out | jq -M . else if [ "$DEBUG_BROWSE_JSON" = true ]; then echo "Recursive Call" >/dev/stderr ShowVariable "recursive" "$recursive" fi "$script" --import $scratchfile --output "$formatstr" --recursive "$recursive" "$param" fi Cleanup exit fi else if [ "$formatstr" = "keys" ]; then echo "${elkey}:{}" >> ${scratchfile}.out else if [ "$formatstr" = "plain" ]; then if [ "$elkey" = "$rparam" ]; then echo "${elvalue}" >> ${scratchfile}.out else echo "${elkey}" >> ${scratchfile}.out fi fi fi fi else if [ "$formatstr" = "keys" ]; then echo "${elkey}:$elvalue" >> ${scratchfile}.out else if [ "$formatstr" = "plain" ]; then if [ "$elkey" = "$rparam" ]; then echo "${elvalue}" >> ${scratchfile}.out else echo "${elkey}" >> ${scratchfile}.out fi else # JSON output printf '{\n "%s": "%s"\n}\n' "${elkey}" "${elvalue}" >> ${scratchfile}.out fi fi fi fi fi fi let i++ done result=`filecheck -s ${scratchfile}.out` if [ "$result" = "${scratchfile}.out" ]; then cat ${scratchfile}.out else if [ "$DEBUG_BROWSE_JSON" = true ]; then ShowVariable "Result - stat" "$stat" fi exitcode=4 errormsg $qopt $exitcode "($progstr) Key '$param' not found in JSON input." Cleanup exit $exitcode fi fi fi fi # Cleanup and exit Cleanup exit $exitcode