Как я могу получить значение поля JSON без `jq`? - PullRequest
1 голос
/ 29 апреля 2020

Я пытаюсь извлечь значение из JSON в ограниченной среде, где я не могу установить какие-либо инструменты или загрузить что-либо из inte rnet. Инструменты, которые у меня есть в этой среде, являются базовыми c, предоставляемыми busybox, такими как: awk, grep и sed. Нет доступных компиляторов или интерпретаторов, таких как Perl и Python.

У JSON, который я пытаюсь разобрать, есть фиксированная схема, но ее можно отформатировать любым допустимым способом, и мне всегда нужно получать значение поля tag.

Примеры возможных JSON:

{"version":1,"name":"2","tag":"3"}
{
    "version": 1,
    "tag":    "3",
    "name"   :"2"
}

Ответы [ 2 ]

0 голосов
/ 29 апреля 2020

Это будет работать для вашего формата данных (т.е. не для возможного полного синтаксиса JSON) с использованием любого awk в любой оболочке на каждом поле UNIX:

$ cat tst.awk
{ rec = rec $0 }
END {
    gsub(/^[ \t]*[{][ \t]*|[ \t]*[}][ \t]*$/,"",rec)
    while ( match(rec,/"[^"]+"[ \t]*:[ \t]]*("[^"]*"|[^,]*)/) ) {
        key = val = substr(rec,RSTART+1,RLENGTH-1)
        sub(/".*/,"",key)
        sub(/[^"]*"[ \t]]*:[ \t]*/,"",val)
        f[key] = val
        rec = substr(rec,RSTART+RLENGTH)
    }
    print f[k]
}

$ echo '{"version":1,"name":"2","tag":"3"}' | awk -v k=tag -f tst.awk
"3"

$ cat file
{
    "version": 1,
    "tag":    "3",
    "name"   :"2"
}

$ awk -v k=tag -f tst.awk file
"3"

Вы можете легко вывести любое значение, которое вам нравится:

$ awk -v k=name -f tst.awk file
"2"

$ awk -v k=version -f tst.awk file
1

, и было бы тривиально изменить вывод нескольких значений в любом порядке, или вывести значение только одной клавиши, если оно находится в диапазоне или основано на отношениях между другие значения ключей, et c., et c. Например:

$ cat tst.awk
{ rec = rec $0 }
END {
    split(keys,ks,/,/)
    gsub(/^[ \t]*[{][ \t]*|[ \t]*[}][ \t]*$/,"",rec)
    while ( match(rec,/"[^"]+"[ \t]*:[ \t]*("[^"]*"|[^,]*)/) ) {
        key = val = substr(rec,RSTART+1,RLENGTH-1)
        sub(/".*/,"",key)
        sub(/[^"]*"[ \t]*:[ \t]*/,"",val)
        f[key] = val
        rec = substr(rec,RSTART+RLENGTH)
    }
    if ( (f["version"] > 0) && (f["name"] != f["tag"]) ) {
        for (i=1; i in ks; i++) {
            k = ks[i]
            print k, f[k]
        }
    }
}

$ awk -v keys=tag,version,name -f tst.awk file
tag "3"
version 1
name "2"

Было бы тривиально обрезать кавычки вокруг значений, если они вам не нужны, просто добавив gsub(/^"|"$/,"",val) в l oop прямо над f[key] = val .

0 голосов
/ 29 апреля 2020

Это может сработать для вас (GNU sed):

sed -nE '$!{:a;N;$!ba;s/\n//g;s/"tag":[^"]*"([^"]*)"/\n\1\nTAG/g};/^[^\n]*\nTAG/P;D' file

Это выкладывает файл в память, удаляет все новые строки, меняет значение тега и тега на отдельные последовательные строки и печатает первую из этих двух строк .

Альтернатива с использованием tr, grep и sed:

tr -d '\n' <file | grep -o '"tag":[^"]*"[^"]*"' | sed -E 's/".*".*"(.*)"/\1/'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...