在bash脚本中使用python函数-解析JSON

在bash脚本中使用python函数-解析JSON,python,json,bash,Python,Json,Bash,我有一个bash脚本,旨在解析JSON格式的数据。我从本论坛上的一个早期查询中了解到,bash不擅长解析JSON,因此打算使用python函数进行解析: 以下是代码片段: #!/bin/bash set -e declare -a data_id declare -a data_tid function parse_data () { python <<EOF import json parsed = json.loads('''${@}''') data_id =

我有一个bash脚本,旨在解析JSON格式的数据。我从本论坛上的一个早期查询中了解到,bash不擅长解析JSON,因此打算使用python函数进行解析:

以下是代码片段:

#!/bin/bash

set -e

declare -a data_id
declare -a data_tid

function parse_data () {
    python <<EOF
import json
parsed = json.loads('''${@}''')

data_id = []
data_tid = []
for child in parsed['params']['children'][0]:
    print 'id: %s' % (child['data']['id'])
    data_id.append(child['data']['id'])
    print 'tid: %s' % (child['data']['tid'])
    data_tid.append(child['data']['tid'])
EOF
}

json=$(get_json_output)
parse_data $json
我希望脚本能够将“数据”下的“id”和“tid”字段提取到单独的数组中。但是脚本执行失败,如下所示:

root@ubuntu# ./abc.sh 
Traceback (most recent call last):
  File "<stdin>", line 7, in <module>
TypeError: string indices must be integers
root@ubuntu#//abc.sh
回溯(最近一次呼叫最后一次):
文件“”,第7行,在
TypeError:字符串索引必须是整数

知道怎么了吗

离开
[0]

for child in parsed['params']['children']:
否则,您将在
子项
列表中的第一个条目的键上循环

或者,如果该列表中只有一个条目,则不要循环,而是直接分配:

child = parsed['params']['children'][0]
print 'id: %s' % (child['data']['id'])
port_id.append(child['data']['id'])
print 'tid: %s' % (child['data']['tid'])
port_tid.append(child['data']['tid'])

您所指的只是儿童列表的第一项。 所以“child”实际上是字典的关键

您应该从FOR循环中删除[0]这一行:

for child in parsed['params']['children'][0]:
    ...
parsed['params']['children'][0]
不是列表

把它改成两个

for child in parsed['params']['children']:
    ...


如果您首先编写Python脚本,然后尝试将其嵌入到bash脚本中,您会发现调试起来要容易得多。以下是调试版本:

import json, sys

parsed = json.load(sys.stdin)

port_id = []
port_tid = []
for child in parsed['params']['children']:
    print 'id: %s' % (child['data']['id'])
    port_id.append(child['data']['id'])
    print 'tid: %s' % (child['data']['tid'])
    port_tid.append(child['data']['tid'])
其次,json数据中有一个bug。我想你的意思是:

{
    "cid": "1",
    "code": null,
    "error": null,
    "params": {
        "children": [
            {
                "value": {
                    "id": "0123456789",
                    "tid": "a.b.c"
                },
                "data": {
                    "id": "0987654321",
                    "tid": "p.q.r"
                },
                "tid": "m.n.o.p"
            },
            {
               "value": {
                    "id": "0987654321",
                    "tid": "a.b.c"
                },
                "data": {
                    "id": "0123456789",
                    "tid": "p.q.r"
                },
                "tid": "m.n.o.p"
            }
        ],
        "tid": "m.n.o.p"
    },
    "reason": null,
    "result": true
}
最后,您仍然需要将输出加载到Bash数组中。以下是我的解决方案:

#!/bin/bash

set -e

parse_ids() { python -c '
import json, sys
parsed = json.load(sys.stdin)
print u"\n".join(c["data"]["id"] for c in parsed["params"]["children"])'
}

parse_tids() { python -c '
import json, sys
parsed = json.load(sys.stdin)
print u"\n".join(c["data"]["tid"] for c in parsed["params"]["children"])'
}

#json=$(get_json_output)
json=$(</dev/stdin)

declare -a port_id
mapfile -t port_id < <(echo "$json" | parse_ids)
echo "${port_id[@]}"

declare -a port_tid
mapfile -t port_tid < <(echo "$json" | parse_tids)
echo "${port_tid[@]}"
#/bin/bash
set-e
parse_id(){python-c'
导入json,sys
parsed=json.load(sys.stdin)
打印u“\n”.join(c[“数据”][“id”]用于解析的c[“参数”][“子项”])”
}
parse_tids(){python-c'
导入json,sys
parsed=json.load(sys.stdin)
打印u“\n”.join(c[“数据”][“tid”]在解析的[“参数”][“子项”]中用于c)
}
#json=$(获取json输出)

json=$(在调用函数时尝试添加引号:
parse_data“$json”
JSON有一个问题:键“value”、“data”和“tid”都在同一个对象中出现多次。这不起作用。@RemcoGerlich:我已经更正了那里缺少的逗号;可能是一个拙劣的复制粘贴。我删除了额外的键。没有用Python编写整个脚本有令人信服的原因吗?@chepner:task要求用bash编写脚本。我观察到,虽然打印效果很好,但这些值并没有附加到数组data_id和data_tid中。数组大小显示为零。@Maddy:你希望Python列表知道你的bash数组吗?那根本不起作用。子进程没有访问bash变量的权限;你没有o让Python进程写入stdout或stderr,在bash脚本中捕获它并从那里开始。没有出路吗?它们不能在脚本中共存并共享数据吗?不,bash的可扩展性不为人知。Python是作为子进程运行的,它不能访问父进程的内存,无论如何也不能没有显式的消息传递.为什么不把所有的东西都移到Python脚本中呢?这样至少就不需要解决这个问题了。
import json, sys

parsed = json.load(sys.stdin)

port_id = []
port_tid = []
for child in parsed['params']['children']:
    print 'id: %s' % (child['data']['id'])
    port_id.append(child['data']['id'])
    print 'tid: %s' % (child['data']['tid'])
    port_tid.append(child['data']['tid'])
{
    "cid": "1",
    "code": null,
    "error": null,
    "params": {
        "children": [
            {
                "value": {
                    "id": "0123456789",
                    "tid": "a.b.c"
                },
                "data": {
                    "id": "0987654321",
                    "tid": "p.q.r"
                },
                "tid": "m.n.o.p"
            },
            {
               "value": {
                    "id": "0987654321",
                    "tid": "a.b.c"
                },
                "data": {
                    "id": "0123456789",
                    "tid": "p.q.r"
                },
                "tid": "m.n.o.p"
            }
        ],
        "tid": "m.n.o.p"
    },
    "reason": null,
    "result": true
}
#!/bin/bash

set -e

parse_ids() { python -c '
import json, sys
parsed = json.load(sys.stdin)
print u"\n".join(c["data"]["id"] for c in parsed["params"]["children"])'
}

parse_tids() { python -c '
import json, sys
parsed = json.load(sys.stdin)
print u"\n".join(c["data"]["tid"] for c in parsed["params"]["children"])'
}

#json=$(get_json_output)
json=$(</dev/stdin)

declare -a port_id
mapfile -t port_id < <(echo "$json" | parse_ids)
echo "${port_id[@]}"

declare -a port_tid
mapfile -t port_tid < <(echo "$json" | parse_tids)
echo "${port_tid[@]}"