ChatGPT解决这个技术问题 Extra ChatGPT

使用 Unix 工具解析 JSON

我正在尝试解析从 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=text 表示)?

呃,不是很好的json解析顺便说一句......字符串中的转义字符怎么样......等等在SO上是否有python对此的答案(甚至是perl答案......)?
每当有人说“问题 X 可以用其他语言 Y 轻松解决”时,这就是 "my toolbox has only a rock for driving nails... why bother with anything else?" 的代码
@BryanH:除了有时语言 Y 可以更有能力解决特定问题 X,而不管建议 Y 的人知道多少种语言。
有点晚了,但它来了。 grep -Po '"'"version"'"\s*:\s*"\K([^"]*)' package.json。这很容易解决任务&仅适用于 grep 并且非常适用于简单的 JSON。对于复杂的 JSON,您应该使用适当的解析器。
@auser,您是否可以将标题中的“使用 sed 和 awk”更改为“使用 UNIX 工具”进行编辑?

C
Chris Noe

有许多专门设计用于从命令行操作 JSON 的工具,它们比使用 Awk 更容易、更可靠,例如 jq

curl -s 'https://api.github.com/users/lambda' | jq -r '.name'

您还可以使用可能已经安装在系统上的工具来执行此操作,例如使用 json module 的 Python,这样可以避免任何额外的依赖关系,同时仍然可以从适当的 JSON 解析器中受益。以下假设您要使用 UTF-8,原始 JSON 应该被编码,并且也是大多数现代终端使用的:

蟒蛇 3:

curl -s 'https://api.github.com/users/lambda' | \
    python3 -c "import sys, json; print(json.load(sys.stdin)['name'])"

蟒蛇2:

export PYTHONIOENCODING=utf8
curl -s 'https://api.github.com/users/lambda' | \
    python2 -c "import sys, json; print json.load(sys.stdin)['name']"

经常问的问题

为什么不是纯外壳解决方案?

标准 POSIX/Single Unix Specification shell 是一种非常有限的语言,它不包含用于表示序列(列表或数组)或关联数组(在某些其他语言中也称为哈希表、映射、字典或对象)的工具。这使得在可移植的 shell 脚本中表示解析 JSON 的结果有些棘手。有 somewhat hacky ways to do it,但如果键或值包含某些特殊字符,它们中的许多可能会中断。

Bash 4 及更高版本、zsh 和 ksh 支持数组和关联数组,但这些 shell 并不普遍可用(由于从 GPLv2 到 GPLv3 的更改,macOS 在 Bash 3 停止更新 Bash,而许多 Linux 系统没有zsh 开箱即用)。您可以编写一个可以在 Bash 4 或 zsh 中运行的脚本,其中一个在当今大多数 macOS、Linux 和 BSD 系统上都可用,但是很难编写一个适用于这样的 shebang 行多语言脚本。

最后,在 shell 中编写一个完整的 JSON 解析器将是一个足够重要的依赖项,您不妨只使用现有的依赖项,如 jq 或 Python。做一个好的实现不会是一个单行,甚至是小的五行代码片段。

为什么不使用 awk、sed 或 grep?

可以使用这些工具从具有已知形状和格式的 JSON 中进行一些快速提取,例如每行一个键。在其他答案中有几个建议示例。

但是,这些工具是为基于行或基于记录的格式而设计的;它们不是为递归解析具有可能转义字符的匹配分隔符而设计的。

因此,这些使用 awk/sed/grep 的快速而肮脏的解决方案可能很脆弱,并且如果输入格式的某些方面发生更改,例如折叠空格,或向 JSON 对象添加额外级别的嵌套,或其中的转义引号,则会中断一个字符串。一个足够强大以处理所有 JSON 输入而不中断的解决方案也将相当大和复杂,因此与添加另一个对 jq 或 Python 的依赖项没有太大区别。

由于之前在 shell 脚本中输入解析不佳,我不得不处理大量的客户数据被删除,所以我从不推荐快速而肮脏的方法,因为这种方法可能很脆弱。如果您正在进行一些一次性处理,请参阅其他答案以获取建议,但我仍然强烈建议您只使用现有的经过测试的 JSON 解析器。

历史笔记

这个答案最初推荐 jsawk,它应该仍然可以工作,但使用起来比 jq 稍微麻烦一些,并且取决于安装的独立 JavaScript 解释器,它比 Python 解释器更不常见,所以上面的答案可能是优选:

curl -s 'https://api.github.com/users/lambda' | jsawk -a 'return this.name'

这个答案最初也使用了问题中的 Twitter API,但该 API 不再有效,因此很难复制示例进行测试,而且新的 Twitter API 需要 API 密钥,因此我已切换到使用 GitHub API无需 API 密钥即可轻松使用。原始问题的第一个答案是:

curl 'http://twitter.com/users/username.json' | jq -r '.text'

@thrau +1。 jq 它在存储库中可用并且超级易于使用,因此它比 jsawk 好得多。我都测试了几分钟,jq赢得了这场战斗
请注意,在 Python 2 中,如果您将输出通过管道传输到另一个命令,则 print 语句将始终编码为 ASCII,因为您在管道中使用 Python。在命令中插入 PYTHONIOENCODING=<desired codec> 以设置适合您终端的不同输出编码。在 Python 3 中,在这种情况下默认为 UTF-8(使用 print() 函数)。
使用 brew install jq 在 OSX 上安装 jq
curl -s 等价于 curl --silent,而 jq -r 表示 jq --raw-output,即没有字符串引号。
python -c "import requests;r=requests.get('api.github.com/users/lambda');print r.json()['name'];" 。最简单的!
J
Jens

为了快速提取特定键的值,我个人喜欢使用“grep -o”,它只返回正则表达式的匹配项。例如,要从推文中获取“文本”字段,例如:

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

这个正则表达式比你想象的更健壮;例如,它可以很好地处理其中嵌入了逗号和转义引号的字符串。我认为,如果它是原子的,你可以做一个实际上保证提取价值的工作。 (如果它有嵌套,那么正则表达式当然不能这样做。)

为了进一步清理(尽管保留字符串的原始转义),您可以使用类似:| perl -pe 's/"text"://; s/^"//; s/",$//'。 (我为 this analysis 做了这个。)

对于所有坚持你应该使用真正的 JSON 解析器的反对者——是的,这对于正确性至关重要,但是

要进行非常快速的分析,例如计算值以检查数据清理错误或大致了解数据,在命令行上敲出一些东西会更快。打开编辑器编写脚本会分散注意力。 grep -o 比 Python 标准 json 库快几个数量级,至少在为推文执行此操作时(每个大约 2 KB)。我不确定这是否只是因为 json 很慢(有时我应该与 yajl 进行比较);但原则上,正则表达式应该更快,因为它是有限状态并且更可优化,而不是必须支持递归的解析器,在这种情况下,会花费大量 CPU 为您不关心的结构构建树。 (如果有人编写了一个有限状态转换器来进行正确的(深度受限的)JSON 解析,那就太棒了!同时我们有“grep -o”。)

为了编写可维护的代码,我总是使用真正的解析库。我没有尝试过 jsawk,但如果它运行良好,那将解决第 1 点。

最后一个更古怪的解决方案:我编写了一个脚本,它使用 Python json 并将所需的键提取到制表符分隔的列中;然后我通过一个围绕 awk 的包装器进行管道传输,该包装器允许对列进行命名访问。 In here: the json2tsv and tsvawk scripts。所以对于这个例子,它将是:

json2tsv id text < tweets.json | tsvawk '{print "tweet " $id " is: " $text}'

这种方法没有解决 #2,比单个 Python 脚本效率低,而且有点脆弱:它强制字符串值中的换行符和制表符标准化,以便与 awk 的字段/记录分隔的世界视图很好地配合。但它确实让您停留在命令行上,比 grep -o 更正确。


您忘记了整数值。 grep -Po '"text":(\d*?,|.*?[^\\]",)'
罗伯特:是的,我的正则表达式只为该字段的字符串值编写。如您所说,可以添加整数。如果你想要所有类型,你必须做的越来越多:布尔值、空值。数组和对象需要更多的工作;在标准正则表达式下,只有深度限制是可能的。
1. jq .name 在命令行上工作,不需要“打开编辑器来编写脚本”。 2. 正则表达式产生错误结果的速度有多快并不重要
如果你只想要这些值,你可以把 awk 扔给它。 | grep -Po '"text":.*?[^\\]",'|awk -F':' '{print $2}'
似乎在 OSX 上缺少 -P 选项。我在 OSX 10.11.5 上测试,grep --versiongrep (BSD grep) 2.5.1-FreeBSD。我让它与 OSX 上的“扩展正则表达式”选项一起工作。上面的命令是 grep -Eo '"text":.*?[^\\]",' tweets.json
P
Peter Mortensen

基于这里的一些建议(尤其是在评论中)建议使用 Python,我很失望没有找到一个例子。

因此,这是从一些 JSON 数据中获取单个值的单行程序。它假设您正在(从某处)管道传输数据,因此在脚本上下文中应该很有用。

echo '{"hostname":"test","domainname":"example.com"}' | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["hostname"]'

我在下面增强了这个答案以使用 bash 函数: curl 'some_api' | getJsonVal '键'
谢谢!为了更快速和更脏的 JSON 解析,我将它包装成一个 bash 函数:jsonq() { python -c "import sys,json; obj=json.load(sys.stdin); print($1)"; },以便我可以编写:curl ...... | jsonq 'json.dumps([key["token"] for key in obj], indent=2)' &更多类似的可怕东西......顺便说一句,obj[0] 似乎没有必要,看起来只有 obj 在默认情况下可以正常工作(?)。
obj[0] 在解析 { "port":5555 } 时会导致错误。删除 [0] 后工作正常。
我得到` File "", line 1 import json,sys;obj=json.load(sys.stdin);print obj["hostname"] ^ SyntaxError: invalid syntax ` when running the example
@ka3ak 最后尝试 print(obj["hostname"]) 而不是 print obj["hostname"]
P
Peter Mortensen

martinr's and Boecko's lead 之后:

curl -s 'http://twitter.com/users/username.json' | python -mjson.tool

这将为您提供非常grep友好的输出。很方便:

curl -s 'http://twitter.com/users/username.json' | python -mjson.tool | grep my_key

正如 OP 所要求的,您将如何提取特定的密钥?
迄今为止最好的答案恕我直言,无需在大多数发行版上安装任何其他东西,您可以| grep field。谢谢!
如果我没记错的话,所有这些都是格式化 JSON。它不允许调用者从输出中选择特定字段,就像 xpath 解决方案或基于“JSON 指针”的东西一样。
我只是得到一个键值对,而不是它本身的值。
jq 通常不安装,而 python 安装。此外,一旦你在 Python 中,你不妨一路走下去,用 import json... 解析它
j
jfs

您只需 download jq binary for your platform 并运行 (chmod +x jq):

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

它从 json 对象中提取 "name" 属性。

jq homepage 表示它类似于 JSON 数据的 sed


仅作记录,jq 是一个了不起的工具。
同意。我无法从接受的答案中与 jsawk 进行比较,因为我没有使用过,但是对于本地实验(可以接受安装工具),我强烈推荐 jq。下面是一个更广泛的示例,它采用数组的每个元素并使用选定的数据合成一个新的 JSON 对象:curl -s https://api.example.com/jobs | jq '.jobs[] | {id, o: .owner.username, dateCreated, s: .status.state}'
喜欢这个。重量很轻,而且因为它是用普通的旧 C 语言编写的,所以几乎可以在任何地方编译。
最实用的一个:它不需要第三方库(而 jsawk 需要)并且易于安装(OSX:brew install jq)
对于我的用例,这是最实用且易于实现的答案。对于 Ubuntu (14.04) 系统,一个简单的 apt-get install jq 将该工具添加到我的系统中。我将来自 AWS CLI 响应的 JSON 输出通过管道传输到 jq 中,它可以很好地将值提取到响应中嵌套的某些键。
P
Peter Mortensen

使用 Node.js

如果系统安装了 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

或任何其他格式,例如包含 JSON 的 URL:

node -pe 'JSON.parse(process.argv[1]).name' "$(curl -s https://api.github.com/users/trevorsenior)"

输出:

Trevor Senior

谢谢!但就我而言,它仅适用于 -e 标志 node -p -e 'JSON.parse(process.argv[1]).foo' '{ "foo": "bar" }'
管道! curl -s https://api.github.com/users/trevorsenior | node -pe "JSON.parse(require('fs').readFileSync('/dev/stdin').toString()).name"
这是我最喜欢的解决方案;使用一种语言(javascript)来解析对它来说很自然的数据结构(JSON)。似乎是最正确的。另外 - 节点可能已经在系统上可用,并且您不必使用 jq 的二进制文件(这看起来是另一个正确的选择)。
这是 bash 脚本函数: # jsonv 获取特定属性的 json 对象值 # 第一个参数是 json 文档 # 第二个参数是应该返回值的属性 get_json_attribute_value() { node -pe 'JSON.parse(process. argv[1])[process.argv[2]]' "$1" "$2" }
以下适用于 Node.js 10:cat package.json | node -pe 'JSON.parse(fs.readFileSync(0)).version'
P
Peter Mortensen

使用 Python's JSON support 而不是使用 AWK!

像这样的东西:

curl -s http://twitter.com/users/username.json | \
    python -c "import json,sys;obj=json.load(sys.stdin);print(obj['name']);"

macOS v12.3 (Monterey) removed /usr/bin/python,因此对于 macOS v12.3 及更高版本,我们必须使用 /usr/bin/python3

curl -s http://twitter.com/users/username.json | \
    python3 -c "import json,sys;obj=json.load(sys.stdin);print(obj['name']);"

请原谅我试图提出一个好的回应......:我会更加努力。党派之争需要的不仅仅是编写一个 awk 脚本来摆脱它!
你为什么在那个 oneliner 解决方案中使用 obj 变量?它没用而且根本不存储?您使用 json.load(sys.stdin)['"key']" 写得更少,例如:curl -sL httpbin.org/ip | python -c "import json,sys; print json.load(sys.stdin)['origin']"
/usr/bin/python 在 macOS 12.3 上不存在,因此现在需要使用 python3。
D
Dennis Williamson

你问过如何在脚上射击自己,而我是来提供弹药的:

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'

我认为其他人已经发出了足够的警报。我会拿着手机等着叫救护车。准备好时开火。


如此疯狂,请阅读:stackoverflow.com/questions/1732348/…
我已经阅读了所有答案,这对我来说非常适合,没有任何额外的依赖。 +1
这就是我一直在寻找的。唯一的更正 - 提供的用于删除引号的 sed 命令对我不起作用,我使用 sed 's/"//g' 代替
J
Joe Heyming

在 Python 中使用 Bash

在 .bashrc 文件中创建一个 Bash 函数:

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 功能的单行:curl http://foo | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["environment"][0]["name"]'
sys.stdout.write() 如果您希望它同时与 python 2 和 3 一起使用。
我认为它应该更改为 system.stdout.write(obj$1)。这样你就可以说:getJsonVal "['environment']['name']",就像@Cheeso 的例子
@Narek 在这种情况下,它看起来像这样:函数 getJsonVal() { py -x "json.dumps(json.loads(x)$1, sort_keys=True, indent=4)"; }
重新“.bash_rc 文件”:不是“.bashrc 文件”(不带下划线)吗?
P
Peter Mortensen

更新 (2020)

我对外部工具(例如 Python)的最大问题是您必须处理包管理器和依赖项才能安装它们。

但是,既然我们将 jq 作为一个独立的静态工具,可以通过 GitHub Releases 和 Webi (webinstall.dev/jq) 轻松跨平台安装,我建议:

Mac、Linux:

curl -sS https://webinstall.dev/jq | bash

视窗 10:

curl.exe -A MS https://webinstall.dev/jq | powershell

备忘单https://webinstall.dev/jq

原创 (2011)

TickTick 是一个用 bash 编写的 JSON 解析器(少于 250 行代码)。

以下是作者的文章摘录,Imagine a world where Bash supports 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

有什么方法可以再次将这个 people 变量打印成 json 字符串?那将非常有用
感谢安装链接,这让我明白了。超级简单。从数组中解压 obj:
链接已损坏。它现在将您带到一个恶意站点,该站点试图在您的浏览器中运行硬币矿工
@spuder:什么链接?有几个。
我刚刚检查了链接。一切对我来说都很好。我的猜测是,一个机器人添加了垃圾链接,而一个 mod 又回来了,后来又修复了它。
P
Peter Mortensen

这是使用大多数 distributions 上可用的标准 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 字符串包含多个电子邮件密钥,解析器将输出 john@doe.com""john@doe.com
如果像 jean-pierre@email.com 这样的电子邮件中有破折号,则不起作用
从技术上讲,sedawk 不是 bash 脚本语言的一部分——它们是外部工具。
@rtc11 你是对的。不幸的是,它不是一个成熟的 JSON 解析器。我在答案中添加了警告。谢谢! 😄
@GwynethLlewelyn 你是绝对正确的。我更正了描述。谢谢! 😄
P
Peter Mortensen

使用 PHP CLI 解析 JSON

这可以说是题外话,但由于优先权占主导地位,这个问题仍然不完整,没有提到我们可信赖和忠实的 PHP,对吗?

它使用相同的示例 JSON,但让我们将其分配给一个变量以减少晦涩难懂。

export JSON='{"hostname":"test","domainname":"example.com"}'

现在为了 PHP 的优点,它使用 file_get_contentsphp://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;'

您甚至可以使用 $argn 而不是 fgets(STDIN)
糟糕,$argn 与 -E 或 -R 标志一起使用,并且仅当 JSON 内容位于一行时...
P
Peter Mortensen

如果有人只想从简单的 JSON 对象中提取值而不需要嵌套结构,则可以使用正则表达式,甚至无需离开 Bash。

这是我使用基于 JSON standard 的 bash 正则表达式定义的函数:

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

Helder Pereira 我们可以用这个函数提取嵌套的属性值吗?
D
Daniel Sokolowski

不幸的是,使用 grep 的最高投票答案返回了在我的场景中不起作用的 full 匹配,但如果您知道 JSON 格式将保持不变,您可以使用 lookbehindlookahead 以仅提取所需的值。

# 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

你永远不会真正知道 JSON 字典中元素的顺序。根据定义,它们是无序的。这正是滚动您自己的 JSON 解析器注定要失败的根本原因之一。
P
Peter Mortensen

使用 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]);"

这是我最喜欢的;)顺便说一句,您可以使用 ruby -rjson 来缩短它以要求该库
请注意,最后的 ; 在 Ruby 中不是必需的(它仅用于将通常位于不同行的语句连接成单行)。
链接已断开 (404)。
S
Stephen Quan

这是另一个 Bash 和 Python 混合的答案。我发布了这个答案,因为我想处理更复杂的 JSON 输出,但是,降低了我的 bash 应用程序的复杂性。我想在 Bash 中从 http://www.arcgis.com/sharing/rest/info?f=json 中打开以下 JSON 对象:

{
  "owningSystemUrl": "http://www.arcgis.com",
  "authInfo": {
    "tokenServicesUrl": "https://www.arcgis.com/sharing/rest/generateToken",
    "isTokenBasedSecurity": true
  }
}

在以下示例中,我利用 Python 创建了自己的 jqunquote 实现。您会注意到,一旦我们将 Python 对象从 json 导入 Python 字典,我们就可以使用 Python 语法来导航字典。要浏览上述内容,语法为:

数据

数据[“authInfo”]

数据[“authInfo”][“tokenServicesUrl”]

通过在 Bash 中使用魔法,我们省略了 data,只在数据右侧提供 Python 文本,即

jq

jq'[“authInfo”]'

jq'[“authInfo”][“tokenServicesUrl”]'

注意,没有参数,jq 充当 JSON 美化器。使用参数,我们可以使用 Python 语法从字典中提取我们想要的任何内容,包括导航子字典和数组元素。

以下是 Bash Python 混合函数:

#!/bin/bash -xe

jq_py() {
  cat <<EOF
import json, sys
data = json.load( sys.stdin )
print( json.dumps( data$1, indent = 4 ) )
EOF
}

jq() {
  python -c "$( jq_py "$1" )"
}

unquote_py() {
  cat <<EOF
import json,sys
print( json.load( sys.stdin ) )
EOF
}

unquote() {
  python -c "$( unquote_py )"
}

以下是 Bash Python 函数的示例用法:

curl http://www.arcgis.com/sharing/rest/info?f=json | tee arcgis.json
# {"owningSystemUrl":"https://www.arcgis.com","authInfo":{"tokenServicesUrl":"https://www.arcgis.com/sharing/rest/generateToken","isTokenBasedSecurity":true}}

cat arcgis.json | jq
# {
#     "owningSystemUrl": "https://www.arcgis.com",
#     "authInfo": {
#         "tokenServicesUrl": "https://www.arcgis.com/sharing/rest/generateToken",
#         "isTokenBasedSecurity": true
#     }
# }

cat arcgis.json | jq '[ "authInfo" ]'
# {
#     "tokenServicesUrl": "https://www.arcgis.com/sharing/rest/generateToken",
#     "isTokenBasedSecurity": true
# }

cat arcgis.json | jq '[ "authInfo" ][ "tokenServicesUrl" ]'
# "https://www.arcgis.com/sharing/rest/generateToken"

cat arcgis.json | jq '[ "authInfo" ][ "tokenServicesUrl" ]' | unquote
# https://www.arcgis.com/sharing/rest/generateToken

P
Peter Mortensen

有一种更简单的方法可以从 JSON 字符串中获取属性。以 package.json 文件为例,试试这个:

#!/usr/bin/env bash
my_val="$(json=$(<package.json) node -pe "JSON.parse(process.env.json)['version']")"

我们使用 process.env,因为这会将文件的内容作为字符串导入 Node.js,而不会有恶意内容转义其引用并被解析为代码的风险。


使用字符串连接将值替换为解析为代码的字符串允许运行任意 node.js 代码,这意味着与您从 Internet 上下载的随机内容一起使用是非常不安全的。在 JavaScript 中解析 JSON 的安全/最佳实践方法不只是评估它是有原因的。
@CharlesDuffy 不确定我是否遵循,但 JSON.parse 调用应该更安全,因为 require() 实际上可以运行外部代码, JSON.parse 不能。
当且仅当您的字符串以绕过解析器的方式实际注入 JSON 运行时时,这是真的。我没有看到这里的代码可靠地做到了这一点。从环境变量中提取它并将其传递给 JSON.parse() 是的,您无疑是安全的......但是在这里,JSON 运行时 接收 带内的(不受信任的)内容(可信)代码。
...类似地,如果您的代码从文件 作为字符串 读取 JSON 并将该字符串传递给 JSON.parse(),那么您也很安全,但这里也不会发生这种情况。
...啊,哎呀,还不如立即进入“如何”。问题在于您将要传递给JSON.parse() 的shell 变量替换为代码。您假设放置文字反引号将保留内容文字,但这是一个完全不安全的假设,因为文字反引号可以存在于文件内容(以及变量)中,因此可以终止引用并输入一个不带引号的上下文,其中值作为代码执行。
P
Peter Mortensen

既然 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'

当然,有一种无需 curl 的纯 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 就是其中之一。这是跨平台版本的 GitHub 页面:PowerShell


我以前不喜欢 PowerShell,但我必须承认将 JSON 作为对象处理非常好。
P
Peter Mortensen

我不能在这里使用任何答案。 jq、shell 数组、declare、grep -P、lookbehind、lookahead、Python、Perl、Ruby 甚至 Bash 都不可用。

剩下的答案根本不起作用。 JavaScript 听起来很熟悉,但罐头上写着 Nescaffe - 所以它也是不行的 :) 即使可用,为了我的简单需求 - 它们会过度杀伤和缓慢。

然而,从我的调制解调器的 JSON 格式回复中获取许多变量对我来说非常重要。我在 Bourne shell (sh) 中做这件事,我的路由器上有一个非常精简的 BusyBox!单独使用 AWK 没有任何问题:只需设置分隔符并读取数据。对于单个变量,仅此而已!

awk 'BEGIN { FS="\""; RS="," }; { if ($2 == "login") {print $4} }' test.json

还记得我没有任何数组吗?我必须在 AWK 中将解析数据分配给我在 shell 脚本中需要的 11 个变量。无论我往哪里看,都说这是一项不可能完成的任务。也没有问题。

我的解决方案很简单。此代码将:

从问题中解析 .json 文件(实际上,我从最受好评的答案中借用了一个工作数据样本)并挑选出引用的数据,并从 awk 中创建 shell 变量,并分配免费的命名 shell 变量名。 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" "" } }' ) 回显 "$Login, $Name, $Updated"

里面的空白没有任何问题。在我的使用中,相同的命令解析一个长的单行输出。使用 eval 时,此解决方案仅适用于受信任的数据。

将其调整为拾取未引用的数据很简单。对于大量变量,使用 else if 可以实现边际速度增益。缺少数组显然意味着:没有额外的摆弄就没有多条记录。但是在阵列可用的情况下,调整此解决方案是一项简单的任务。

@maikel 的 sed 答案几乎可以工作(但我不能对此发表评论)。对于我格式良好的数据 - 它有效。与此处使用的示例无关(缺少引号将其丢弃)。它复杂且难以修改。另外,我不喜欢必须进行 11 次调用来提取 11 个变量。为什么?我对提取 9 个变量的 100 次循环进行了计时:sed 函数耗时 48.99 秒,而我的解决方案耗时 0.91 秒!不公平?只提取 9 个变量:0.51 与 0.02 秒。


j
jaypal singh

你可以试试这样的 -

curl -s 'http://twitter.com/users/jaypalsingh.json' | 
awk -F=":" -v RS="," '$1~/"text"/ {print}'

P
Peter Mortensen

也有 XML 文件的人可能想查看我的 Xidel。它是一个命令行界面、无依赖性的 JSONiq 处理器。 (即,它还支持用于 XML 或 JSON 处理的 XQuery。)

问题中的示例是:

 xidel -e 'json("http://twitter.com/users/username.json")("name")'

或者使用我自己的非标准扩展语法:

 xidel -e 'json("http://twitter.com/users/username.json").name'

或者现在更简单:xidel -s https://api.github.com/users/lambda -e 'name'(或 -e '$json/name',或 -e '($json).name')。
k
kev

您可以使用 jshon

curl 'http://twitter.com/users/username.json' | jshon -e text

该网站说:“两倍的速度,1/6 的内存”......然后:“Jshon 解析、读取和创建 JSON。它被设计为在 shell 内尽可能可用,并替换由grep/sed/awk 以及由 perl/python 制成的重量级单行解析器。"
这被列为在 Bash 中解析 JSON 的推荐解决方案
摆脱结果周围引号的最简单方法是什么?
I
Inian

现有答案中未涵盖的一个有趣工具是使用 gron written in Go,它的标语是 Make JSON greppable!,这正是它的作用。

因此,基本上 gron 将您的 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";

它还支持带有 -s 命令行标志的流式 JSON,您可以在其中连续地对输入流进行 gron 匹配记录。此外,gron 的运行时依赖项为零。您可以download a binary在 Linux、Mac、Windows 或 FreeBSD 上运行它。

更多使用示例和旅行可以在官方 Github 页面找到 - Advanced Usage

至于为什么您可以使用 gron 而不是其他 JSON 解析工具,请参阅项目页面的作者注释。

为什么我不应该只使用 jq?

jq 很棒,而且比 gron 强大得多,但这种力量带来了复杂性。 gron 旨在让您更容易使用您已经知道的工具,例如 grep 和 sed。


嗯,我从来没有听说过格伦。凉爽的!
P
Peter Mortensen

以下是使用 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
        }
    }
}'

P
Peter Mortensen

对于更复杂的 JSON 解析,我建议使用 Python jsonpath 模块(由 Stefan Goessner 编写) -

安装它 - sudo easy_install -U jsonpath 使用它 - 示例 file.json (来自 http://goessner.net/articles/JsonPath) - { "store": { "book": [ { "category": "reference", “作者”:“奈杰尔·里斯”,“标题”:“世纪谚语”,“价格”:8.95 },{“类别”:“小说”,“作者”:“伊芙琳·沃”,“标题”:“荣誉之剑”,“价格”:12.99 },{“类别”:“小说”,“作者”:“赫尔曼梅尔维尔”,“标题”:“白鲸记”,“isbn”:“0-553-21311- 3", "price": 8.99 }, { "category": "fiction", "author": "JRR 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') )" 将输出 - 世纪白鲸记 注意:上面的命令行不包括错误检查。对于带有错误检查的完整解决方案,您应该创建一个小的 Python 脚本,并使用 try-except 包装代码。


我在安装 jsonpath 时遇到了一点问题,所以安装了 jsonpath_rw,所以如果上述方法不起作用,您可以尝试以下类似的方法:1) /usr/bin/python -m pip install jsonpath-rw 2) cat ~/trash/file.json | /usr/bin/python -c "from jsonpath_rw import jsonpath, parse; import sys,json; jsonpath_expr = parse('store.book[0]'); out = [match.value for match in jsonpath_expr.find(json.load(sys.stdin))]; print out;"(我使用了完整路径python 二进制文件,因为我在安装多个 python 时遇到了一些问题)。
P
Peter Mortensen

如果您安装了 PHP 解释器:

php -r 'var_export(json_decode(`curl http://twitter.com/users/username.json`, 1));'

例如:

我们有一个资源,它提供带有国家 ISO codes: http://country.io/iso3.json 的 JSON 内容,我们可以很容易地在带有 curl 的 shell 中看到它:

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',
  ...

如果您有嵌套数组,则此输出看起来会更好...


P
Peter Mortensen

还有一个非常简单但功能强大的 JSON CLI 处理工具 fx

https://i.stack.imgur.com/OE1cE.png

例子

使用匿名函数:

echo '{"key": "value"}' | fx "x => x.key"

输出:

value

如果不传递匿名函数参数 → ...,代码将自动转换为匿名函数。您可以通过此关键字访问 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。无需学习新语法。

更高版本的 fx 具有交互模式! -


如果您要宣传自己的创作,则需要明确说明。请参阅How not to be a spammer.
R
RussellStewart

这是 pythonpy 的一个很好的用例:

curl 'http://twitter.com/users/username.json' | py 'json.load(sys.stdin)["name"]'

更短,这里的 python -c 模块 :) 很好。
N
Nathan Tuggy

Here is a good reference。在这种情况下:

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]} }  }'

这个答案应该得到最高的投票,如果不是所有其他答案,大多数(如果不是全部)都依赖于包(php、python 等)。
不,相反,任何带有 useless use of sed 的东西都不应再收到任何赞成票。
SecKarma,没错!话题说UNIX工具对吗? Tripleee,有任何 ON TOPIC 示例代码供我们查看吗?
A
Alexey Dubkov

如果 pip 在系统上可用,则:

$ pip install json-query

使用示例:

$ curl -s http://0/file.json | json-query
{
    "key":"value"    
}

$ curl -s http://0/file.json | json-query my.key
value

$ curl -s http://0/file.json | json-query my.keys.
key_1
key_2
key_3

$ curl -s http://0/file.json | json-query my.keys.2
value_2

如何查找 json-array 长度使用 json-query 包?