Разбор JSON с инструментами Unix
Я пытаюсь проанализировать JSON, возвращенный из запроса curl, вот так:
curl 'http://twitter.com/users/username.json' |
sed -e 's/[{}]/''/g' |
awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}'
Выше разбивает JSON на поля, например:
% ...
"geo_enabled":false
"friends_count":245
"profile_text_color":"000000"
"status":"in_reply_to_screen_name":null
"source":"web"
"truncated":false
"text":"My status"
"favorited":false
% ...
Как напечатать определенное поле (обозначено -v k=text
)?
46 ответов
Существует целый ряд инструментов, специально разработанных для манипулирования JSON из командной строки, и он будет намного проще и надежнее, чем с Awk, например: jq
:
curl -s 'https://api.github.com/users/lambda' | jq -r '.name'
Вы также можете сделать это с помощью инструментов, которые, вероятно, уже установлены в вашей системе, например, Python, использующий json
модуль, и поэтому избегайте каких-либо дополнительных зависимостей, при этом все еще имея преимущество правильного парсера JSON. Далее предполагается, что вы хотите использовать UTF-8, в который должен быть закодирован исходный JSON и который используется большинством современных терминалов:
Python 2:
export PYTHONIOENCODING=utf8
curl -s 'https://api.github.com/users/lambda' | \
python -c "import sys, json; print json.load(sys.stdin)['name']"
Python 3:
curl -s 'https://api.github.com/users/lambda' | \
python3 -c "import sys, json; print(json.load(sys.stdin)['name'])"
Исторические заметки
Этот ответ изначально рекомендовал jsawk, который все еще должен работать, но его использование немного сложнее, чем jq
и зависит от устанавливаемого автономного интерпретатора JavaScript, который менее распространен, чем интерпретатор Python, поэтому приведенные выше ответы, вероятно, предпочтительнее:
curl -s 'https://api.github.com/users/lambda' | jsawk -a 'return this.name'
В этом ответе также изначально использовался API-интерфейс Twitter из этого вопроса, но этот API-интерфейс больше не работает, что затрудняет копирование примеров для тестирования, а новому API-интерфейсу Twitter требуются ключи API, поэтому я перешел на использование GitHub API, который можно легко использовать без ключей API. Первый ответ на оригинальный вопрос будет:
curl 'http://twitter.com/users/username.json' | jq -r '.text'
Чтобы быстро извлечь значения для конкретного ключа, мне лично нравится использовать "grep -o", который возвращает только совпадение с регулярным выражением. Например, чтобы получить поле "текст" из твитов, что-то вроде:
grep -Po '"text":.*?[^\\]",' tweets.json
Это регулярное выражение более надежно, чем вы думаете; например, он прекрасно работает со строками, в которых есть запятые и экранированные кавычки. Я думаю, немного проделав работу, вы могли бы сделать такую, которая действительно гарантированно извлечет значение, если оно атомарное. (Если у него есть вложенность, то, конечно, регулярное выражение не может этого сделать.)
И для дальнейшей очистки (хотя и сохраняя исходное экранирование строки) вы можете использовать что-то вроде: | perl -pe 's/"text"://; s/^"//; s/",$//'
, (Я сделал это для этого анализа.)
Всем ненавистникам, которые настаивают на том, что вы должны использовать настоящий анализатор JSON - да, это важно для правильности, но
- Чтобы провести действительно быстрый анализ, например, подсчет значений для проверки ошибок при очистке данных или получить общее представление о данных, вывод чего-либо в командной строке происходит быстрее. Открытие редактора для написания сценария отвлекает.
grep -o
это на порядки быстрее, чем стандарт Pythonjson
библиотека, по крайней мере, при этом для твитов (которые ~2 КБ каждый). Я не уверен, что это только потому, чтоjson
медленный (я должен сравнить с yajl когда-нибудь); но в принципе, регулярное выражение должно быть быстрее, поскольку оно является конечным состоянием и гораздо более оптимизируемым, вместо синтаксического анализатора, который должен поддерживать рекурсию, и в этом случае тратит много деревьев построения ЦП для структур, которые вам не нужны. (Если бы кто-то написал преобразователь конечного состояния, который выполнял правильный (ограниченный по глубине) анализ JSON, это было бы фантастически! Тем временем у нас есть "grep -o".)
Для написания поддерживаемого кода я всегда использую настоящую библиотеку синтаксического анализа. Я не пробовал jsawk, но если он будет работать хорошо, это будет точка № 1.
Последнее, более странное решение: я написал скрипт, использующий Python json
и извлекает нужные ключи в столбцы, разделенные табуляцией; тогда я трубу через обертку вокруг awk
это позволяет именованный доступ к столбцам. Здесь: сценарии json2tsv и tsvawk. Так что для этого примера это будет:
json2tsv id text < tweets.json | tsvawk '{print "tweet " $id " is: " $text}'
Этот подход не касается #2, он более неэффективен, чем отдельный скрипт Python, и он немного хрупок: он заставляет нормализовать переводы строк и табуляции в строковых значениях, чтобы хорошо сочетаться с представлением awk в поле / записи с разделителями в виде мира. Но это позволяет вам оставаться в командной строке, с большей точностью, чем grep -o
,
На основании того, что некоторые из рекомендаций здесь (особенно в комментариях) предполагают использование Python, я был разочарован тем, что не нашел пример.
Итак, вот один вкладыш для получения одного значения из некоторых данных JSON. Предполагается, что вы передаете данные (откуда-то) и поэтому должны быть полезны в контексте сценариев.
echo '{"hostname":"test","domainname":"example.com"}' | python -c 'import json,sys;obj=json.load(sys.stdin);print obj[0]["hostname"]'
Вслед за MartinR и Бекко лидируют:
$ curl -s 'http://twitter.com/users/username.json' | python -mjson.tool
Это даст вам невероятно дружественный результат. Очень удобно:
$ curl -s 'http://twitter.com/users/username.json' | python -mjson.tool | grep my_key
Вы можете просто скачать jq
бинарный для вашей платформы и запуска (chmod +x jq
):
$ curl 'https://twitter.com/users/username.json' | ./jq -r '.name'
Извлекает "name"
атрибут из объекта JSON.
jq
домашняя страница говорит, что это как sed
для данных JSON.
Использование Node.js
Если в системе установлен узел, можно использовать -p
распечатать и -e
вывести флаги сценария JSON.parse
вытащить любое значение, которое необходимо.
Простой пример использования строки JSON { "foo": "bar" }
и вытаскивая значение "foo":
$ node -pe 'JSON.parse(process.argv[1]).foo' '{ "foo": "bar" }'
bar
Потому что у нас есть доступ к cat
и другие утилиты, мы можем использовать это для файлов:
$ node -pe 'JSON.parse(process.argv[1]).foo' "$(cat foobar.json)"
bar
Или любой другой формат, такой как URL, который содержит JSON:
$ node -pe 'JSON.parse(process.argv[1]).name' "$(curl -s https://api.github.com/users/trevorsenior)"
Trevor Senior
Используйте поддержку Python JSON вместо awk!
Что-то вроде этого:
curl -s http://twitter.com/users/username.json | \
python -c "import json,sys;obj=json.load(sys.stdin);print obj['name'];"
Вы спросили, как выстрелить себе в ногу, и я здесь, чтобы предоставить боеприпасы:
curl -s 'http://twitter.com/users/username.json' | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^text/ {print $2}'
Вы могли бы использовать tr -d '{}'
вместо sed
, Но полное их исключение, похоже, также дает желаемый эффект.
Если вы хотите удалить внешние кавычки, передайте результат вышеупомянутого через sed 's/\(^"\|"$\)//g'
Я думаю, что другие забили достаточно тревоги. Я буду стоять с сотовым телефоном, чтобы вызвать скорую помощь. Огонь, когда будете готовы.
TickTick - это анализатор JSON, написанный на bash (<250 строк кода)
Вот отрывок из его статьи " Представьте мир, в котором Bash поддерживает JSON":
#!/bin/bash
. ticktick.sh
``
people = {
"Writers": [
"Rod Serling",
"Charles Beaumont",
"Richard Matheson"
],
"Cast": {
"Rod Serling": { "Episodes": 156 },
"Martin Landau": { "Episodes": 2 },
"William Shatner": { "Episodes": 2 }
}
}
``
function printDirectors() {
echo " The ``people.Directors.length()`` Directors are:"
for director in ``people.Directors.items()``; do
printf " - %s\n" ${!director}
done
}
`` people.Directors = [ "John Brahm", "Douglas Heyes" ] ``
printDirectors
newDirector="Lamont Johnson"
`` people.Directors.push($newDirector) ``
printDirectors
echo "Shifted: "``people.Directors.shift()``
printDirectors
echo "Popped: "``people.Directors.pop()``
printDirectors
Использование Bash с Python
Создайте функцию bash в вашем файле.bash_rc
function getJsonVal () {
python -c "import json,sys;sys.stdout.write(json.dumps(json.load(sys.stdin)$1))";
}
затем
$ curl 'http://twitter.com/users/username.json' | getJsonVal "['text']"
My status
$
Здесь та же функция, но с проверкой ошибок.
function getJsonVal() {
if [ \( $# -ne 1 \) -o \( -t 0 \) ]; then
cat <<EOF
Usage: getJsonVal 'key' < /tmp/
-- or --
cat /tmp/input | getJsonVal 'key'
EOF
return;
fi;
python -c "import json,sys;sys.stdout.write(json.dumps(json.load(sys.stdin)$1))";
}
Где $# -ne 1 обеспечивает как минимум 1 ввод, а -t 0 - перенаправление с канала.
Приятной особенностью этой реализации является то, что вы можете получить доступ к вложенным значениям json и получить взамен json! знак равно
Пример:
$ echo '{"foo": {"bar": "baz", "a": [1,2,3]}}' | getJsonVal "['foo']['a'][1]"
2
Если вы хотите быть по-настоящему модным, можете распечатать данные:
function getJsonVal () {
python -c "import json,sys;sys.stdout.write(json.dumps(json.load(sys.stdin)$1, sort_keys=True, indent=4))";
}
$ echo '{"foo": {"bar": "baz", "a": [1,2,3]}}' | getJsonVal "['foo']"
{
"a": [
1,
2,
3
],
"bar": "baz"
}
Родная версия Bash: также хорошо работает с обратной косой чертой (\) и кавычками (")
function parse_json()
{
echo $1 | \
sed -e 's/[{}]/''/g' | \
sed -e 's/", "/'\",\"'/g' | \
sed -e 's/" ,"/'\",\"'/g' | \
sed -e 's/" , "/'\",\"'/g' | \
sed -e 's/","/'\"---SEPERATOR---\"'/g' | \
awk -F=':' -v RS='---SEPERATOR---' "\$1~/\"$2\"/ {print}" | \
sed -e "s/\"$2\"://" | \
tr -d "\n\t" | \
sed -e 's/\\"/"/g' | \
sed -e 's/\\\\/\\/g' | \
sed -e 's/^[ \t]*//g' | \
sed -e 's/^"//' -e 's/"$//'
}
parse_json '{"username":"john, doe","email":"john@doe.com"}' username
parse_json '{"username":"john doe","email":"john@doe.com"}' email
--- outputs ---
john, doe
johh@doe.com
Разбор JSON с PHP CLI
Возможно, не по теме, но, поскольку царит приоритет, этот вопрос остается неполным без упоминания о нашем верном и верном PHP, я прав?
Используя тот же пример JSON, но давайте присвоим его переменной, чтобы уменьшить затенение.
$ export JSON='{"hostname":"test","domainname":"example.com"}'
Теперь для бога PHP, используя file_get_contents и обертку потока php://stdin.
$ echo $JSON|php -r 'echo json_decode(file_get_contents("php://stdin"))->hostname;'
или как указано с использованием fgets и уже открытого потока с постоянной CLD STI.
$ echo $JSON|php -r 'echo json_decode(fgets(STDIN))->hostname;'
NJoy!
Если кто-то просто хочет извлечь значения из простых объектов JSON без необходимости использования вложенных структур, можно использовать регулярные выражения, даже не выходя из bash.
Вот функция, которую я определил, используя регулярные выражения bash на основе стандарта JSON:
function json_extract() {
local key=$1
local json=$2
local string_regex='"([^"\]|\\.)*"'
local number_regex='-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?'
local value_regex="${string_regex}|${number_regex}|true|false|null"
local pair_regex="\"${key}\"[[:space:]]*:[[:space:]]*(${value_regex})"
if [[ ${json} =~ ${pair_regex} ]]; then
echo $(sed 's/^"\|"$//g' <<< "${BASH_REMATCH[1]}")
else
return 1
fi
}
Предостережения: объекты и массивы не поддерживаются как значения, но поддерживаются все другие типы значений, определенные в стандарте. Кроме того, пара будет сопоставлена независимо от того, насколько глубоко она находится в документе JSON, если она имеет одно и то же имя ключа.
Используя пример OP:
$ json_extract text "$(curl 'http://twitter.com/users/username.json')"
My status
$ json_extract friends_count "$(curl 'http://twitter.com/users/username.json')"
245
К сожалению, топ проголосовал ответ, который использует grep
возвращает полное совпадение, которое не сработало в моем сценарии, но если вы знаете, что формат JSON останется постоянным, вы можете использовать lookbehind и lookahead, чтобы извлечь только нужные значения.
# echo '{"TotalPages":33,"FooBar":"he\"llo","anotherValue":100}' | grep -Po '(?<="FooBar":")(.*?)(?=",)'
he\"llo
# echo '{"TotalPages":33,"FooBar":"he\"llo","anotherValue":100}' | grep -Po '(?<="TotalPages":)(.*?)(?=,)'
33
# echo '{"TotalPages":33,"FooBar":"he\"llo","anotherValue":100}' | grep -Po '(?<="anotherValue":)(.*?)(?=})'
100
Версия, которая использует Ruby и http://flori.github.com/json/
$ < file.json ruby -e "require 'rubygems'; require 'json'; puts JSON.pretty_generate(JSON[STDIN.read]);"
или более кратко:
$ < file.json ruby -r rubygems -r json -e "puts JSON.pretty_generate(JSON[STDIN.read]);"
Это еще один bash
& python
гибридный ответ. Я опубликовал этот ответ, потому что хотел обработать более сложный вывод JSON, но, уменьшив сложность моего приложения bash. Я хочу взломать следующий объект JSON с http://www.arcgis.com/sharing/rest/info?f=json в bash
:
{
"owningSystemUrl": "http://www.arcgis.com",
"authInfo": {
"tokenServicesUrl": "https://www.arcgis.com/sharing/rest/generateToken",
"isTokenBasedSecurity": true
}
}
Хотя этот подход увеличивает сложность функции Python, использование bash упрощается:
function jsonGet {
python -c 'import json,sys
o=json.load(sys.stdin)
k="'$1'"
if k != "":
for a in k.split("."):
if isinstance(o, dict):
o=o[a] if a in o else ""
elif isinstance(o, list):
if a == "length":
o=str(len(o))
elif a == "join":
o=",".join(o)
else:
o=o[int(a)]
else:
o=""
if isinstance(o, str) or isinstance(o, unicode):
print o
else:
print json.dumps(o)
'
}
curl -s http://www.arcgis.com/sharing/rest/info?f=json | jsonGet
curl -s http://www.arcgis.com/sharing/rest/info?f=json | jsonGet authInfo
curl -s http://www.arcgis.com/sharing/rest/info?f=json | jsonGet authInfo.tokenServicesUrl
Вывод вышеприведенного скрипта:
- {"owningSystemUrl": " http://www.arcgis.com/", "authInfo": {"tokenServicesUrl": " https://www.arcgis.com/sharing/rest/generateToken", "isTokenBasedSecurity": true} }
- {"tokenServicesUrl": " https://www.arcgis.com/sharing/rest/generateToken", "isTokenBasedSecurity": true}
- https://www.arcgis.com/sharing/rest/generateToken
Я добавил поддержку массивов, чтобы вы могли использовать .length
и, если источником является строковый массив, вы можете использовать .join
:
curl -s http://www.arcgis.com/sharing/rest/portals/self?f=pjson | jsonGet defaultBasemap.baseMapLayers.length
curl -s http://www.arcgis.com/sharing/rest/portals/self?f=pjson | jsonGet defaultBasemap.baseMapLayers.0.resourceInfo.tileInfo.lods
curl -s http://www.arcgis.com/sharing/rest/portals/self?f=pjson | jsonGet defaultBasemap.baseMapLayers.0.resourceInfo.tileInfo.lods.length
curl -s http://www.arcgis.com/sharing/rest/portals/self?f=pjson | jsonGet defaultBasemap.baseMapLayers.0.resourceInfo.tileInfo.lods.23
Какие выводы:
- 1
- [{"scale": 591657527.591555, "разрешение": 156543.03392800014, "уровень": 0}, {"масштаб": 295828763.795777, "разрешение": 78271.51696399994, "уровень": 1}, {"масштаб": 147914381.897889, "разрешение" ": 39135.75848200009," уровень ": 2}, {" масштаб ": 73957190.948944," разрешение ": 19567.87924099992," уровень ": 3}, {" масштаб ": 36978595.474472," разрешение ": 9783.93962049996," уровень ": 4}, {"scale": 18489297.737236, "разрешение": 4891.96981024998, "уровень": 5}, {"масштаб": 9244648.868618, "разрешение": 2445.98490512499, "уровень": 6}, {"масштаб": 4622324.434309, "разрешение" ": 1222.992452562495," уровень ": 7}, {" масштаб ": 2311162.217155," разрешение ": 611.4962262813797," уровень ": 8}, {" масштаб ": 1155581.108577," разрешение ": 305.74811314055756," уровень ": 9}, {"scale": 577790.554289, "разрешение": 152.87405657041106, "уровень": 10}, {"масштаб": 288895.277144, "разрешение": 76.43702828507324, "уровень": 11}, {"масштаб": 144447.638572, "разрешение" ": 38.21851414253662," уровень ": 12}, {" масштаб ": 72223.819286," разрешение ": 19.1092570712683 1, "уровень": 13}, {"масштаб": 36111.909643, "разрешение": 9.554628535634155, "уровень": 14}, {"масштаб": 18055.954822, "разрешение": 4.77731426794937, "уровень": 15}, { "scale": 9027.977411, "resolution": 2.388657133974685, "level": 16}, {"scale": 4513.988705, "resolution": 1.1943285668550503, "level": 17}, {"scale": 2256.994353, "resolution": 0.5971642835598172, "уровень": 18}, {"масштаб": 1128.497176, "разрешение": 0.29858214164761665, "уровень": 19}, {"масштаб": 564.248588, "разрешение": 0.14929107082380833, "уровень": 20}, { "scale": 282.124294, "resolution": 0.07464553541190416, "level": 21}, {"scale": 141.062147, "resolution": 0.03732276770595208, "level": 22}, {"scale": 70.5310735, "resolution": 0.01866138385297604, "уровень": 23}]
- 24
- {"scale": 70.5310735, "resolution": 0.01866138385297604, "level": 23}
Есть более простой способ получить свойство из строки json. Используя package.json
файл в качестве примера, попробуйте это:
#!/usr/bin/env bash
str=`cat package.json`;
my_val="$(node -pe "JSON.parse(\`$str\`)['version']")"
или же
#!/usr/bin/env bash
prop="version"
my_val="$(node -pe "require('./package.json')['$prop']")"
Обе техники прекрасно работают. наслаждаться.
подробнее читайте здесь: Свойство read name файла package.json с помощью bash
Теперь, когда Powershell является кроссплатформенным, я подумал, что смогу найти выход, так как считаю его довольно интуитивным и чрезвычайно простым.
curl -s 'https://api.github.com/users/lambda' | ConvertFrom-Json
ConvertFrom-Json преобразует JSON в пользовательский объект Powershell, поэтому вы можете легко работать со свойствами с этого момента. Например, если вам нужно только свойство id, вы просто сделаете это:
curl -s 'https://api.github.com/users/lambda' | ConvertFrom-Json | select -ExpandProperty id
Если вы хотите вызвать все это изнутри Bash, то вам придется называть это так:
powershell 'curl -s "https://api.github.com/users/lambda" | ConvertFrom-Json'
Конечно, есть чистый способ Powershell сделать это без curl, который будет:
Invoke-WebRequest 'https://api.github.com/users/lambda' | select -ExpandProperty Content | ConvertFrom-Json
Наконец, есть также ConvertTo-Json, который так же легко преобразует пользовательский объект в JSON. Вот пример:
(New-Object PsObject -Property @{ Name = "Tester"; SomeList = @('one','two','three')}) | ConvertTo-Json
Что дало бы хороший JSON вот так:
{
"Name": "Tester",
"SomeList": [
"one",
"two",
"three"
]
}
По общему признанию, использование оболочки Windows в Unix несколько кощунственно, но Powershell действительно хорош в некоторых вещах, и разбор JSON и XML - пара из них. Эта страница GitHub для кроссплатформенной версии https://github.com/PowerShell/PowerShell
Я не могу использовать ни один из ответов здесь. Нет доступных jq, нет массивов оболочек, нет объявлений, нет grep -P, нет look -hind и lookahead, нет Python, нет Perl, нет Ruby, нет - даже нет Bash... Остальные ответы просто не работают хорошо. JavaScript звучит знакомо, но в банке написано Nescaffe - так что это тоже не пойдет:) Даже если таковые имеются, для моей простой необходимости - они будут излишними и медленными.
Тем не менее, для меня чрезвычайно важно получить много переменных из ответа моего модема в формате json. Я делаю это в sh с очень урезанным BusyBox на моих маршрутизаторах! Нет проблем с использованием только awk: просто установите разделители и прочитайте данные. Для одной переменной это все!
awk 'BEGIN { FS="\""; RS="," }; { if ($2 == "login") {print $4} }' test.json
Помните, у меня нет массивов? Я должен был присвоить анализируемые в awk данные 11 переменным, которые мне нужны в сценарии оболочки. Куда бы я ни посмотрел, это считалось невыполнимой миссией. С этим тоже проблем нет.
Мое решение простое. Этот код будет: 1) анализировать файл.json из вопроса (на самом деле, я позаимствовал образец рабочих данных из наиболее часто задаваемого ответа) и выбрать данные в кавычках, плюс 2) создать переменные оболочки из awk, присваивая свободную именованную оболочку имена переменных.
eval $( curl -s 'https://api.github.com/users/lambda' |
awk ' BEGIN { FS="\""; RS="," };
{
if ($2 == "login") { print "Login=\""$4"\"" }
if ($2 == "name") { print "Name=\""$4"\"" }
if ($2 == "updated_at") { print "Updated=\""$4"\"" }
}' )
echo "$Login, $Name, $Updated"
Никаких проблем с пробелами внутри. В моем случае эта же команда анализирует длинный однострочный вывод. Поскольку используется eval, это решение подходит только для доверенных данных. Его легко адаптировать для сбора данных без кавычек. Для огромного числа переменных, предельное увеличение скорости может быть достигнуто с помощью else if. Отсутствие массива, очевидно, означает: нет многократных записей без лишних действий. Но там, где имеются массивы, адаптация этого решения - простая задача.
@maikel sed ответ почти работает (но я не могу это комментировать). Для моих красиво отформатированных данных - это работает. Не так много с примером, использованным здесь (пропущенные кавычки отбрасывают его). Это сложно и сложно изменить. Кроме того, мне не нравится делать 11 вызовов для извлечения 11 переменных. Зачем? Я рассчитал 100 циклов, извлекая 9 переменных: функция sed заняла 48,99 с, а мое решение заняло 0,91 с! Не честно? Выполнение только одного извлечения из 9 переменных: 0,51 против 0,02 сек.
Кто-то, у кого также есть XML-файлы, может захотеть взглянуть на мой Xidel. Это Cli, JSONiq процессор без зависимостей. (т.е. он также поддерживает XQuery для обработки XML или JSON)
Пример в вопросе будет:
xidel -e 'json("http://twitter.com/users/username.json")("name")'
Или с моим собственным нестандартным синтаксисом расширения:
xidel -e 'json("http://twitter.com/users/username.json").name'
Один интересный инструмент, который не был охвачен в существующих ответах, - это использование написанного на Go, который имеет слоган, который гласит: Make JSON greppable!что именно он и делает.
Таким образом, по сути, ваш JSON разбивается на дискретные назначения, чтобы увидеть абсолютный «путь» к нему. Основное преимущество этого инструмента перед другими инструментами, такими как
jq
было бы разрешить поиск значения, не зная, в какой вложенной записи присутствует поиск, без нарушения исходной структуры JSON.
например, я хочу найти
'twitter_username'
поле по следующей ссылке, я просто делаю
% gron 'https://api.github.com/users/lambda' | fgrep 'twitter_username'
json.twitter_username = "unlambda";
% gron 'https://api.github.com/users/lambda' | fgrep 'twitter_username' | gron -u
{
"twitter_username": "unlambda"
}
Так просто, как, что. Обратите внимание, как
gron -u
(сокращение от ungron) восстанавливает JSON обратно из пути поиска. Нужда в
fgrep
просто фильтровать ваш поиск по нужным путям и не позволять выражению поиска оценивать как регулярное выражение, а как фиксированную строку (которая, по сути,
grep -F
)
Другой пример поиска строки, чтобы увидеть, где во вложенной структуре находится запись.
% echo '{"foo":{"bar":{"zoo":{"moo":"fine"}}}}' | gron | fgrep "fine"
json.foo.bar.zoo.moo = "fine";
Он также поддерживает потоковую передачу JSON с его
-s
флаг командной строки, с помощью которого вы можете постоянно обрабатывать входной поток для соответствующей записи. Также не имеет зависимостей во время выполнения. Вы можете скачать двоичный файл для Linux, Mac, Windows или FreeBSD и запустить его.
Больше примеров использования и поездок можно найти на официальной странице Github - Advanced Usage
Что касается того, почему вы можете использовать
gron
о других инструментах синтаксического анализа JSON см. в примечании автора на странице проекта.
Почему бы мне просто не использовать jq?
jq потрясающий и намного более мощный, чем gron, но с этой мощью приходит сложность. gron стремится упростить использование уже знакомых вам инструментов, таких как grep и sed.
Вы можете попробовать что-то вроде этого -
curl -s 'http://twitter.com/users/jaypalsingh.json' |
awk -F=":" -v RS="," '$1~/"text"/ {print}'
Вот один способ, которым вы можете сделать это с помощью awk
curl -sL 'http://twitter.com/users/username.json' | awk -F"," -v k="text" '{
gsub(/{|}/,"")
for(i=1;i<=NF;i++){
if ( $i ~ k ){
print $i
}
}
}'
Существует также очень простой, но мощный инструмент обработки JSON CLI - https://github.com/antonmedv/fx
Примеры
Используйте анонимную функцию:
$ echo '{"key": "value"}' | fx "x => x.key"
value
Если вы не передадите анонимную функцию param => ..., код будет автоматически преобразован в анонимную функцию. И вы можете получить доступ к JSON по этому ключевому слову:
$ echo '[1,2,3]' | fx "this.map(x => x * 2)"
[2, 4, 6]
Или просто используйте точечный синтаксис:
$ echo '{"items": {"one": 1}}' | fx .items.one
1
Вы можете передать любое количество анонимных функций для сокращения JSON:
$ echo '{"items": ["one", "two"]}' | fx "this.items" "this[1]"
two
Вы можете обновить существующий JSON с помощью оператора распространения:
$ echo '{"count": 0}' | fx "{...this, count: 1}"
{"count": 1}
Просто простой JavaScript. Не нужно изучать новый синтаксис.
ОБНОВЛЕНИЕ 2018-11-06
fx
теперь есть интерактивный режим (!)
Ты можешь использовать jshon
:
curl 'http://twitter.com/users/username.json' | jshon -e text
Вот хорошая ссылка. В этом случае:
curl 'http://twitter.com/users/username.json' | sed -e 's/[{}]/''/g' | awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) { where = match(a[i], /\"text\"/); if(where) {print a[i]} } }'
Мне нужно было что-то короткое в BASH, и оно работало бы без зависимостей, кроме стандартных Linux LSB и Mac OS для обоих Python 2.7 и 3, и обрабатывало ошибки, например, сообщало бы об ошибках синтаксического анализа json и пропущенных свойствах без выкидывания исключений python:
json-extract () {
if [[ "$1" == "" || "$1" == "-h" || "$1" == "-?" || "$1" == "--help" ]] ; then
echo 'Extract top level property value from json document'
echo ' Usage: json-extract <property> [ <file-path> ]'
echo ' Example 1: json-extract status /tmp/response.json'
echo ' Example 2: echo $JSON_STRING | json-extract-file status'
echo ' Status codes: 0 - success, 1 - json parse error, 2 - property missing'
else
python -c $'import sys, json;\ntry: obj = json.load(open(sys.argv[2])); \nexcept: sys.exit(1)\ntry: print(obj[sys.argv[1]])\nexcept: sys.exit(2)' "$1" "${2:-/dev/stdin}"
fi
}
Разбор JSON является болезненным в сценарии оболочки. Используя более подходящий язык, создайте инструмент, который извлекает атрибуты JSON способом, соответствующим соглашениям сценариев оболочки. Вы можете использовать свой новый инструмент, чтобы решить непосредственную проблему сценариев оболочки, а затем добавить его в свой комплект для будущих ситуаций.
Например, рассмотрим инструмент jsonlookup такой, что если я скажу jsonlookup access token id
он вернет идентификатор атрибута, определенный в маркере атрибута, определенном в атрибуте access из stdin, который, предположительно, является данными JSON. Если атрибут не существует, инструмент ничего не возвращает (состояние выхода 1). Если синтаксический анализ завершился неудачно, выйдите из состояния 2 и отправьте сообщение в stderr. Если поиск успешен, инструмент печатает значение атрибута.
Создав инструмент Unix для точной цели извлечения значений JSON, вы можете легко использовать его в сценариях оболочки:
access_token=$(curl <some horrible crap> | jsonlookup access token id)
Любой язык подойдет для реализации jsonlookup. Вот довольно лаконичная версия Python:
#!/usr/bin/python
import sys
import json
try: rep = json.loads(sys.stdin.read())
except:
sys.stderr.write(sys.argv[0] + ": unable to parse JSON from stdin\n")
sys.exit(2)
for key in sys.argv[1:]:
if key not in rep:
sys.exit(1)
rep = rep[key]
print rep
Для более сложного анализа JSON я предлагаю использовать модуль Python jsonpath (автор Stefan Goessner) -
- Установите это -
sudo easy_install -U jsonpath
- Используй это -
Пример file.json (из http://goessner.net/articles/JsonPath) -
{ "store": {
"book": [
{ "category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{ "category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{ "category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{ "category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
}
}
Разобрать (извлечь все названия книг с ценой < 10) -
$ cat file.json | python -c "import sys, json, jsonpath; print '\n'.join(jsonpath.jsonpath(json.load(sys.stdin), 'store.book[?(@.price < 10)].title'))"
Будет выходной -
Sayings of the Century
Moby Dick
ПРИМЕЧАНИЕ. Приведенная выше командная строка не включает проверку ошибок. для полного решения с проверкой ошибок вы должны создать небольшой скрипт на python и обернуть код в try-exc.
Если у вас есть php:
php -r 'var_export(json_decode(`curl http://twitter.com/users/username.json`, 1));'
Например:
у нас есть ресурс, который предоставляет json коды iso стран: http://country.io/iso3.json и мы легко можем увидеть его в оболочке с curl:
curl http://country.io/iso3.json
но выглядит не очень удобно и не читабельно, лучше разбирать json и видеть читаемую структуру:
php -r 'var_export(json_decode(`curl http://country.io/iso3.json`, 1));'
Этот код напечатает что-то вроде:
array (
'BD' => 'BGD',
'BE' => 'BEL',
'BF' => 'BFA',
'BG' => 'BGR',
'BA' => 'BIH',
'BB' => 'BRB',
'WF' => 'WLF',
'BL' => 'BLM',
...
если у вас есть вложенные массивы, этот вывод будет выглядеть намного лучше...
Надеюсь, это будет полезно...