├── example.json ├── README.md ├── LICENSE └── bash-json-parser /example.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": "value of string", 3 | "number": -125.0, 4 | "boolean": true, 5 | "array": [ 6 | { 7 | "object": "object value" 8 | }, 9 | "plain string", 10 | 2, 11 | false 12 | ] 13 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | bash-json-parser 2 | ================ 3 | 4 | A little bash script that converts a JSON string into key value pairs. This script has no dependencies to other tools like `grep` or `sed`, it relies purely on bash commands. 5 | 6 | If you want better performance or more configuration options, I suggest using other solutions like [jq](https://github.com/stedolan/jq) or [JSON.sh](https://github.com/dominictarr/JSON.sh). Both projects can be found here at Github. 7 | 8 | Usage 9 | ----- 10 | 11 | You can either use a pipe or parameters to convert your JSON string into a list of key-value-pairs. 12 | 13 | cat example.json | ./bash-json-parser 14 | ./bash-json-parser '{ "a": "b", "c": [ "hallo", "welt" ] }' 15 | 16 | The latter will produce the following output 17 | 18 | a=b 19 | c.0=hallo 20 | c.1=welt 21 | 22 | Hints 23 | ----- 24 | 25 | This script does not validate your input, so if your input has an incorrect syntax, I can not promise, that the output is in any way usable. 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Florian Kalis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /bash-json-parser: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function output_entry() { 4 | echo "$1=$2" 5 | } 6 | 7 | function parse_array() { 8 | local current_path="${1:+$1.}$2" 9 | local current_scope="root" 10 | local current_index=0 11 | 12 | while [ "$chars_read" -lt "$INPUT_LENGTH" ]; do 13 | [ "$preserve_current_char" == "0" ] && chars_read=$((chars_read+1)) && read -r -s -n 1 c 14 | preserve_current_char=0 15 | c=${c:-' '} 16 | 17 | case "$current_scope" in 18 | "root") # Waiting for new object or value 19 | case "$c" in 20 | '{') 21 | parse_object "$current_path" "$current_index" 22 | current_scope="entry_separator" 23 | ;; 24 | ']') 25 | return 26 | ;; 27 | [\"tfTF\-0-9]) 28 | preserve_current_char=1 # Let the parse value function decide what kind of value this is 29 | parse_value "$current_path" "$current_index" 30 | preserve_current_char=1 # Parse value has terminated with a separator or an array end, but we can handle this only in the next while iteration 31 | current_scope="entry_separator" 32 | ;; 33 | 34 | esac 35 | ;; 36 | "entry_separator") 37 | [ "$c" == "," ] && current_index=$((current_index+1)) && current_scope="root" 38 | [ "$c" == "]" ] && return 39 | ;; 40 | esac 41 | done 42 | } 43 | 44 | function parse_value() { 45 | local current_path="${1:+$1.}$2" 46 | local current_scope="root" 47 | 48 | while [ "$chars_read" -lt "$INPUT_LENGTH" ]; do 49 | [ "$preserve_current_char" == "0" ] && chars_read=$((chars_read+1)) && read -r -s -n 1 c 50 | preserve_current_char=0 51 | c=${c:-' '} 52 | 53 | case "$current_scope" in 54 | "root") # Waiting for new string, number or boolean 55 | case "$c" in 56 | '"') # String begin 57 | current_scope="string" 58 | current_varvalue="" 59 | ;; 60 | [\-0-9]) # Number begin 61 | current_scope="number" 62 | current_varvalue="$c" 63 | ;; 64 | [tfTF]) # True or false begin 65 | current_scope="boolean" 66 | current_varvalue="$c" 67 | ;; 68 | "[") # Array begin 69 | parse_array "" "$current_path" 70 | return 71 | ;; 72 | "{") # Object begin 73 | parse_object "" "$current_path" 74 | return 75 | esac 76 | ;; 77 | "string") # Waiting for string end 78 | case "$c" in 79 | '"') # String end if not in escape mode, normal character otherwise 80 | [ "$current_escaping" == "0" ] && output_entry "$current_path" "$current_varvalue" && return 81 | [ "$current_escaping" == "1" ] && current_varvalue="$current_varvalue$c" && current_escaping=0 82 | ;; 83 | '\') # Escape character, entering or leaving escape mode 84 | [ "$current_escaping" == "1" ] && current_varvalue="$current_varvalue$c" 85 | current_escaping=$((1-current_escaping)) 86 | ;; 87 | *) # Any other string character 88 | current_escaping=0 89 | current_varvalue="$current_varvalue$c" 90 | ;; 91 | esac 92 | ;; 93 | "number") # Waiting for number end 94 | case "$c" in 95 | [,\]}]) # Separator or array end or object end 96 | output_entry "$current_path" "$current_varvalue" 97 | preserve_current_char=1 # The caller needs to handle this char 98 | return 99 | ;; 100 | [\-0-9.]) # Number can only contain digits, dots and a sign 101 | current_varvalue="$current_varvalue$c" 102 | ;; 103 | # Ignore everything else 104 | esac 105 | ;; 106 | "boolean") # Waiting for boolean to end 107 | case "$c" in 108 | [,\]}]) # Separator or array end or object end 109 | output_entry "$current_path" "$current_varvalue" 110 | preserve_current_char=1 # The caller needs to handle this char 111 | return 112 | ;; 113 | [a-zA-Z]) # No need to do some strict checking, we do not want to validate the incoming json data 114 | current_varvalue="$current_varvalue$c" 115 | ;; 116 | # Ignore everything else 117 | esac 118 | ;; 119 | esac 120 | done 121 | } 122 | 123 | function parse_object() { 124 | local current_path="${1:+$1.}$2" 125 | local current_scope="root" 126 | 127 | while [ "$chars_read" -lt "$INPUT_LENGTH" ]; do 128 | [ "$preserve_current_char" == "0" ] && chars_read=$((chars_read+1)) && read -r -s -n 1 c 129 | preserve_current_char=0 130 | c=${c:-' '} 131 | 132 | case "$current_scope" in 133 | "root") # Waiting for new field or object end 134 | [ "$c" == "}" ] && return 135 | [ "$c" == "\"" ] && current_scope="varname" && current_varname="" && current_escaping=0 136 | ;; 137 | "varname") # Reading the field name 138 | case "$c" in 139 | '"') # String end if not in escape mode, normal character otherwise 140 | [ "$current_escaping" == "0" ] && current_scope="key_value_separator" 141 | [ "$current_escaping" == "1" ] && current_varname="$current_varname$c" && current_escaping=0 142 | ;; 143 | '\') # Escape character, entering or leaving escape mode 144 | current_escaping=$((1-current_escaping)) 145 | current_varname="$current_varname$c" 146 | ;; 147 | *) # Any other string character 148 | current_escaping=0 149 | current_varname="$current_varname$c" 150 | ;; 151 | esac 152 | ;; 153 | "key_value_separator") # Waiting for the key value separator (:) 154 | [ "$c" == ":" ] && parse_value "$current_path" "$current_varname" && current_scope="field_separator" 155 | ;; 156 | "field_separator") # Waiting for the field separator (,) 157 | [ "$c" == ',' ] && current_scope="root" 158 | [ "$c" == '}' ] && return 159 | ;; 160 | esac 161 | done 162 | } 163 | 164 | function parse() { 165 | chars_read=0 166 | preserve_current_char=0 167 | 168 | while [ "$chars_read" -lt "$INPUT_LENGTH" ]; do 169 | read -r -s -n 1 c 170 | c=${c:-' '} 171 | chars_read=$((chars_read+1)) 172 | 173 | # A valid JSON string consists of exactly one object 174 | [ "$c" == "{" ] && parse_object "" "" && return 175 | # ... or one array 176 | [ "$c" == "[" ] && parse_array "" "" && return 177 | 178 | done 179 | } 180 | 181 | if [ -z "$@" ]; then 182 | INPUT=$(cat -) 183 | else 184 | INPUT=$(echo "$@") 185 | fi 186 | 187 | INPUT_LENGTH="${#INPUT}" 188 | parse "" "" <<< "${INPUT}" 189 | 190 | --------------------------------------------------------------------------------