Разбор 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 ]

19 голосов
/ 24 января 2013

Разбор 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 и уже открытый поток с постоянной CLI STDIN .

$ echo $JSON|php -r 'echo json_decode(fgets(STDIN))->hostname;'

NJoy!

18 голосов
/ 30 октября 2014

Родная версия 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
12 голосов
/ 27 апреля 2011

Версия, которая использует 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]);"
10 голосов
/ 20 ноября 2016

К сожалению, ответ с наибольшим количеством голосов, использующий 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
6 голосов
/ 20 марта 2017

Теперь, когда 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 сделать это без скручивания, который будет:

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

5 голосов
/ 11 апреля 2012

Вы можете использовать jshon:

curl 'http://twitter.com/users/username.json' | jshon -e text
5 голосов
/ 20 сентября 2017

Если кто-то просто хочет извлечь значения из простых объектов 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
5 голосов
/ 30 мая 2013

Кто-то, у кого также есть 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'
5 голосов
/ 18 октября 2018

Я не могу использовать ни один из ответов здесь. Нет доступных 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 с.

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

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