Разбор JSON с инструментами Unix - PullRequest
717 голосов
/ 24 декабря 2009

Я пытаюсь проанализировать 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)?

Ответы [ 36 ]

869 голосов
/ 24 декабря 2009

Существует ряд инструментов, специально разработанных для манипулирования 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'
256 голосов
/ 28 июля 2011

Чтобы быстро извлечь значения для определенного ключа, мне лично нравится использовать «grep -o», который возвращает только совпадение с регулярным выражением. Например, чтобы получить текстовое поле из твитов, что-то вроде:

grep -Po '"text":.*?[^\\]",' tweets.json

Это регулярное выражение более надежно, чем вы думаете; например, он прекрасно работает со строками, имеющими запятые и экранированные кавычки внутри них. Я думаю, немного проделав работу, вы могли бы сделать такую, которая действительно гарантированно извлечет значение, если оно атомарное. (Если у него есть вложенность, то, конечно, регулярное выражение не может этого сделать.)

А для дальнейшей очистки (хотя и сохраняя исходное экранирование строки) вы можете использовать что-то вроде: | perl -pe 's/"text"://; s/^"//; s/",$//'. (Я сделал это для этого анализа .)

Всем ненавистникам, которые настаивают на том, что вы должны использовать настоящий анализатор JSON - да, это важно для правильности, но

  1. Чтобы провести действительно быстрый анализ, например, подсчет значений для проверки ошибок очистки данных или общее представление о данных, вывести что-либо в командную строку быстрее. Открытие редактора для написания сценария отвлекает.
  2. grep -o на несколько порядков быстрее, чем стандартная библиотека Python json, по крайней мере при выполнении этого для твитов (каждый размером ~ 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.

160 голосов
/ 06 декабря 2011

На основании того, что некоторые из рекомендаций здесь (особенно в комментариях) предполагают использование Python, я был разочарован тем, что не нашел пример.

Итак, вот одна строка для получения одного значения из некоторых данных JSON. Предполагается, что вы передаете данные (откуда-то) и поэтому должны быть полезны в контексте сценариев.

echo '{"hostname":"test","domainname":"example.com"}' | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["hostname"]'
125 голосов
/ 12 июня 2011

Вслед за Мартином и Беко:

$ 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
120 голосов
/ 30 мая 2013

Вы можете просто загрузить jq бинарный файл для вашей платформы и запустить (chmod +x jq):

$ curl 'https://twitter.com/users/username.json' | ./jq -r '.name'

Извлекает атрибут "name" из объекта json.

jq домашняя страница говорит, что это похоже на sed для данных JSON.

94 голосов
/ 24 декабря 2009

Используйте Поддержка Python JSON вместо использования awk!

Примерно так:

curl -s http://twitter.com/users/username.json | \
    python -c "import json,sys;obj=json.load(sys.stdin);print obj['name'];"
89 голосов
/ 27 августа 2013

Использование Node.js

Если в системе установлен , можно использовать флаги сценариев -p print и -e evaulate с помощью 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
54 голосов
/ 24 декабря 2009

Вы спросили, как выстрелить себе в ногу, и я здесь, чтобы предоставить боеприпасы:

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'

Я думаю, что другие забили достаточно тревоги. Я буду стоять с сотовым телефоном, чтобы вызвать скорую помощь. Огонь, когда будешь готов.

40 голосов
/ 06 февраля 2014

Использование 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"
}
25 голосов
/ 10 декабря 2011

TickTick - это анализатор JSON, написанный на bash (<250 строк кода) </p>

Вот отрывок автора из его статьи, Представьте себе мир, в котором 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...