如何在Shell脚本中压缩JSON?

39
我在寻找一种方法来美化一些JSON并在我的bash控制台中使用。这有助于之后在另一个命令中使用它(例如,在内联方式下将json传递给 httpie)。
给定:
{
    "foo": "lorem",
    "bar": "ipsum"
}

我想要获得:

{"foo":"lorem","bar":"ipsum"}

注意: 这个问题受到了 其格式化打印的对应问题 的很大启发,但是谷歌搜索 bash minify json 没有给我一个合适的结果,因此提出这个问题来进行压缩/混淆。


建议:在这里的示例字符串中添加一些空格。 - orion elenzil
我不确定你说的是什么意思@orionelenzil,但如果你指的是带有空格的字符串(" lorem. "),我不希望它们在之后被压缩。顺便说一下,这个问题只是一个例子,你会看到答案还使用了一个外部的、更大的json文件。 - Ulysse BN
没错,这只是一个小问题,但是根据你现有的示例,简单地去除空格是可以正常工作的。但是如果值或键中存在空格,那么这种简单的方法就不适用了,因此需要使用一些更高级的方法,比如答案中提到的建议。 - orion elenzil
不好意思,我似乎不明白你想要什么...无论如何,我宁愿保持简单,这不是一个使用案例,只是一个例子! - Ulysse BN
5个回答

51

您可以使用jq-c(紧凑)选项。

jq -c . < input.json


如果您有一个深度嵌套的JSON文件,则无法工作。请参见:https://dev59.com/r38QtIcB2Jgan1zn9cTw - Cornelius Roemer
事实上,jq 的递归限制似乎为 512,请参见https://dev59.com/r38QtIcB2Jgan1zn9cTw#75524782,根据基准测试结果,`jj` 更快! - Cornelius Roemer
cat input.json | jq -c > output.json 这种方法对于大文件来说更加方便和简洁。 - undefined

26

简短概述

无需安装

python -c 'import json, sys;json.dump(json.load(sys.stdin), sys.stdout)' < my.json

非常快速(使用jj

jj -u < my.json

性能基准测试

以下是使用hyperfine执行的脚本:

#!/usr/bin/env bash

tmp=$(mktemp json.XXX)
tmp_md=$(mktemp md.XXX)

trap "rm $tmp $tmp_md" EXIT

cat <<JSON > $tmp
{
    "foo": "lorem",
    "bar": "ipsum"
}
JSON
hyperfine \
    --export-markdown $tmp_md \
    --warmup 100 \
    "jj -u < $tmp" \
    "yq eval -j -I=0 < $tmp" \
    "xidel -s - -e '\$json' --printed-json-format=compact < $tmp" \
    "jq --compact-output < $tmp" \
    "python3 -c 'import json, sys;json.dump(json.load(sys.stdin), sys.stdout)' < $tmp" \
    "ruby -r json -e 'j JSON.parse \$stdin.read' < $tmp"

pbcopy < $tmp_md

我的Mac电脑 — MacBook Air (M1, 2020), 8GB内存的结果:

命令 平均时间 [毫秒] 最短时间 [毫秒] 最长时间 [毫秒] 相对值
jj -u < json.p72 1.3 ± 0.2 0.9 2.7 1.00
yq eval -j -I=0 < json.p72 4.4 ± 0.4 3.8 7.8 3.37 ± 0.65
xidel -s - -e '$json' --printed-json-format=compact < json.p72 5.5 ± 0.3 5.0 6.5 4.19 ± 0.77
python3 -c 'import json, sys;json.dump(json.load(sys.stdin), sys.stdout)' < json.p72 14.0 ± 0.4 13.4 15.0 10.71 ± 1.89
jq --compact-output < json.p72 14.4 ± 2.0 13.2 33.6 11.02 ± 2.45
ruby -r json -e 'j JSON.parse $stdin.read' < json.p72 47.3 ± 0.6 46.1 48.5 36.10 ± 6.32

一个大的JSON文件(14k行)的结果:

http https://france-geojson.gregoiredavid.fr/repo/regions.geojson | jj -p > $tmp
命令 平均 [ms] 最少 [ms] 最多 [ms] 相对值
jj -u < json.wFY 3.4 ± 0.7 2.7 12.2 1.00
jq --compact-output < json.wFY 35.1 ± 0.4 34.5 36.1 10.24 ± 2.23
python3 -c 'import json, sys;json.dump(json.load(sys.stdin), sys.stdout)' < json.wFY 47.4 ± 0.5 46.3 48.7 13.82 ± 3.01
xidel -s - -e '$json' --printed-json-format=compact < json.wFY 55.5 ± 1.2 54.7 63.5 16.17 ± 3.53
ruby -r json -e 'j JSON.parse $stdin.read' < json.wFY 94.9 ± 0.7 93.8 96.8 27.65 ± 6.02
yq eval -j -I=0 < json.wFY 3087.0 ± 26.6 3049.3 3126.8 899.63 ± 195.81

这里是漂亮的打印结果相应的基准测试。


1
uglify-js 怎么样? - GChuf
如果你在谈论这个网站:http://lisperator.net/uglifyjs/,那它是一个JavaScript代码混淆器(uglifyer),而不是JSON。同时相较于Node,Python更常见于电脑上。因此我不确定它是否是一个好的选择 :/ - Ulysse BN
1
Python代码示例:python -c 'import json, sys;json.dump(json.load(sys.stdin), sys.stdout)' < myfile.json - Grant Foster
jj很棒,而且在深度嵌套的树中不会抛出错误。jq的递归限制为512。 - Cornelius Roemer
1
“不需要安装”Python命令行在这里对我产生了没有完全压缩的JSON。它在 :, 后面有空格。jq -c 去除了这些空格。 - orion elenzil
显示剩余6条评论

1

1
我已经将其加入基准测试!它似乎比 jq 更快! - Ulysse BN

0

jq-minify

这是一个 Bash 脚本,可以将压缩后的内容写回到文件中。

适用于 bash v3.2+ 和 jq v1.6+。

#!/usr/bin/env bash
set -eu
path=
options=()
# change -c to -r to get pretty-print
set -- "$@" -c .
for arg; do
  if [ -f "$arg" ]; then
    if [ -n "$path" ]; then
      echo "Cannot specify multiple paths to jq-minify" >&2
      exit 1
    fi
    path="$arg"
  else
    options+=("$arg")
  fi
done
tmp=$(mktemp)
jq "${options[@]}" "$path" >"$tmp"
cat "$tmp" >"$path"

1
我不确定我看到这个脚本的重点在哪里,为什么不使用 jq -c file > file1 - Ulysse BN

0

使用

xidel -s input.json -e '$json' --printed-json-format=compact
#or
xidel -s input.json -e 'serialize-json($json)'
{"foo": "lorem", "bar": "ipsum"}

有趣的“基准测试”,Ulysse BN。
我无法测试jj,但在我的旧CPU上,这是我的结果:

var='{
    "foo": "lorem",
    "bar": "ipsum"
}'

time (for i in {1..100}; do python -c 'import json, sys;json.dump(json.load(sys.stdin), sys.stdout)' <<< "$var" >& /dev/null; done)

real    0m10.813s
user    0m7.532s
sys     0m5.798s

time (for i in {1..100}; do jq --compact-output <<< "$var" >& /dev/null; done)

real    0m10.500s
user    0m1.835s
sys     0m0.769s

time (for i in {1..100}; do xidel -se '$json' --printed-json-format=compact <<< "$var" >& /dev/null; done)

real    0m2.250s
user    0m1.692s
sys     0m0.889s

非常有趣的解决方案!我已经将其添加到我的基准测试中,以便更容易阅读答案 :) - Ulysse BN

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接