我试图解析从curl请求返回的JSON,就像这样:

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=文本表示)?


当前回答

使用Ruby进行解析(解释器默认在/usr/bin/ruby中可用于所有macOS版本):

echo "${JSON}" | /usr/bin/ruby -e 'require "json"; puts JSON.parse(http://STDIN.read)["key1"]["nested_key_2"];'

其他回答

更新(2020)

我使用外部工具(例如Python)时遇到的最大问题是,你必须处理包管理器和安装它们的依赖关系。

然而,现在我们有了jq作为一个独立的静态工具,很容易通过GitHub发布和Webi (webinstall.dev/jq)跨平台安装,我建议:

Mac、Linux:

curl -sS https://webi.sh/jq | bash

Windows 10:

curl.exe -A MS https://webi.ms/jq | powershell

小抄:https://webinstall.dev/jq

原(2011)

TickTick是一个用bash编写的JSON解析器(不到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

这是使用大多数发行版上可用的标准Unix工具。它也适用于反斜杠(\)和引号(")。

警告:这并不能接近jq的功能,并且只能用于非常简单的JSON对象。这是在无法安装其他工具的情况下尝试回答最初的问题。

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响应,如下所示:

curl $url | grep $var | awk '{print $2}' | sed s/\"//g

显然,这里的$url将是Twitter url, $var将是“text”,以获取该变量的响应。

实际上,我认为我所做的OP所遗漏的唯一一件事是grep,用于他所寻找的特定变量的行。AWK获取行上的第二项,并使用sed删除引号。

比我聪明的人可能会用AWK或grep来做整个思考。

现在,你可以用sed完成这一切:

curl $url | sed '/text/!d' | sed s/\"text\"://g | sed s/\"//g | sed s/\ //g

因此,没有AWK,没有grep…我不知道为什么我以前没想到。嗯…

既然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"
             ]

}

诚然,在Unix上使用Windows shell有点亵渎神明,但PowerShell确实擅长某些事情,解析JSON和XML就是其中之一。这是跨平台版本PowerShell的GitHub页面

使用PHP CLI解析JSON

这可以说是跑题了,但是由于优先级的原因,如果不提到我们值得信赖和忠诚的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;'