使用 readarray 将 json 字典转换为 bash 哈希表

发布于 2025-01-12 07:17:25 字数 1165 浏览 0 评论 0原文

首先是一个数组的工作示例

json_array() {
    local -n array="${1}"
    readarray -d $'\0' -t array < <(
        # Create nul delimited array entry using jq
        jq -cjn --argjson arr "$array" '$arr|map(tostring)|.[]+"\u0000"'
    )
}

> unset arr; arr='["a", "b", "c"]'; json_array arr; echo "${arr[0]} ${arr[1]} ${arr[2]}"
a b c

现在我尝试用 dict 做类似的事情,将 json dict 转换为 bash 关联数组

json_dict() {
    local -n dict="${1}"
    declare -A hash_table

    append_to_hash_table() {
        shift
        { read -r key;  read -r value; } <<<"$1"
        hash_table+=([$key]="$value")
    }

    readarray -d $'\0' -c 1 -C append_to_hash_table < <(
        # Create nul delimited dict entry using jq
        jq -cjn --argjson d "$dict" '$d|to_entries|map("\(.key)\n\(.value|tostring|@sh)")|.[]+"\u0000"'
    )

    # Here hash_table contain the correct output
    dict=""
    dict="$hash_table"
}

> unset arr; arr='{"a": "aa", "l": "bb", "c": "ccccc"}'; json_dict arr; echo "${arr[@]}"
Nothing

似乎 dict="$hash_table" 没有正确更新引用名称, 如何使 bash dict refname 指向 hash_table

First a working example with arrays

json_array() {
    local -n array="${1}"
    readarray -d 

Now I'm trying to do something similar with dict, convert a json dict into a bash associative array

json_dict() {
    local -n dict="${1}"
    declare -A hash_table

    append_to_hash_table() {
        shift
        { read -r key;  read -r value; } <<<"$1"
        hash_table+=([$key]="$value")
    }

    readarray -d 

It seems dict="$hash_table" doesn't correctly update the refname,
How can I make bash dict refname point to hash_table?

\0' -t array < <( # Create nul delimited array entry using jq jq -cjn --argjson arr "$array" '$arr|map(tostring)|.[]+"\u0000"' ) } > unset arr; arr='["a", "b", "c"]'; json_array arr; echo "${arr[0]} ${arr[1]} ${arr[2]}" a b c

Now I'm trying to do something similar with dict, convert a json dict into a bash associative array


It seems dict="$hash_table" doesn't correctly update the refname,
How can I make bash dict refname point to hash_table?

\0' -c 1 -C append_to_hash_table < <( # Create nul delimited dict entry using jq jq -cjn --argjson d "$dict" '$d|to_entries|map("\(.key)\n\(.value|tostring|@sh)")|.[]+"\u0000"' ) # Here hash_table contain the correct output dict="" dict="$hash_table" } > unset arr; arr='{"a": "aa", "l": "bb", "c": "ccccc"}'; json_dict arr; echo "${arr[@]}" Nothing

It seems dict="$hash_table" doesn't correctly update the refname,
How can I make bash dict refname point to hash_table?

\0' -t array < <( # Create nul delimited array entry using jq jq -cjn --argjson arr "$array" '$arr|map(tostring)|.[]+"\u0000"' ) } > unset arr; arr='["a", "b", "c"]'; json_array arr; echo "${arr[0]} ${arr[1]} ${arr[2]}" a b c

Now I'm trying to do something similar with dict, convert a json dict into a bash associative array

It seems dict="$hash_table" doesn't correctly update the refname,
How can I make bash dict refname point to hash_table?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一个人的旅程 2025-01-19 07:17:26

这里不需要 readarray:您可以将两个单独的 NUL 分隔的 read 作为 while 循环的一部分。

请参阅 https://replit.com/@CharlesDuffy2/GrandioseDraftyArguments#main 演示的以下答案。 sh

while IFS= read -r -d '' key && IFS= read -r -d '' value; do
  hash_table[$key]=$value
done < <(jq -cjn --argjson d "$arr" \
           '$d | to_entries[] | ( .key, "\u0000", .value, "\u0000")')

将其放入上下文中:

json_dict() {
  declare key value in_value="${!1}"
  unset "$1"                   # FIXME: Better to take a $2 for output variable
  declare -g -A "$1"
  declare -n hash_table="$1"
  while IFS= read -r -d '' key && IFS= read -r -d '' value; do
    hash_table[$key]=$value
  done < <(
    jq -cjn --argjson d "$in_value" \
      '$d | to_entries[] | ( .key, "\u0000", .value, "\u0000")'
  )
}
 
arr='{"a": "aa", "l": "bb", "c": "ccccc"}'
json_dict arr
declare -p arr

...作为输出发出:

declare -A arr=([a]="aa" [c]="ccccc" [l]="bb" )

也就是说,要完全按照要求回答问题,因此使用readarray:

json_dict() {
  declare -a pieces=()

  readarray -d '' pieces < <(
    jq -cjn --argjson d "${!1}" \
      '$d | to_entries[] | ( .key, "\u0000", .value, "\u0000")'
  )

  unset "$1"
  declare -g -A "$1"
  declare -n hash_table="$1"
  set -- "${pieces[@]}"

  while (( $# )); do
    hash_table[$1]=$2
    { shift && shift; } || return
  done
}

arr='{"a": "aa", "l": "bb", "c": "ccccc"}'
json_dict arr
declare -p arr

There's no need for readarray here: You can have two separate NUL-delimited reads as part of your while loop.

See the below answer demonstrated at https://replit.com/@CharlesDuffy2/GrandioseDraftyArguments#main.sh

while IFS= read -r -d '' key && IFS= read -r -d '' value; do
  hash_table[$key]=$value
done < <(jq -cjn --argjson d "$arr" \
           '$d | to_entries[] | ( .key, "\u0000", .value, "\u0000")')

Putting this into context:

json_dict() {
  declare key value in_value="${!1}"
  unset "$1"                   # FIXME: Better to take a $2 for output variable
  declare -g -A "$1"
  declare -n hash_table="$1"
  while IFS= read -r -d '' key && IFS= read -r -d '' value; do
    hash_table[$key]=$value
  done < <(
    jq -cjn --argjson d "$in_value" \
      '$d | to_entries[] | ( .key, "\u0000", .value, "\u0000")'
  )
}
 
arr='{"a": "aa", "l": "bb", "c": "ccccc"}'
json_dict arr
declare -p arr

...emits as output:

declare -A arr=([a]="aa" [c]="ccccc" [l]="bb" )

That said, to answer the question exactly as-asked, thus using readarray:

json_dict() {
  declare -a pieces=()

  readarray -d '' pieces < <(
    jq -cjn --argjson d "${!1}" \
      '$d | to_entries[] | ( .key, "\u0000", .value, "\u0000")'
  )

  unset "$1"
  declare -g -A "$1"
  declare -n hash_table="$1"
  set -- "${pieces[@]}"

  while (( $# )); do
    hash_table[$1]=$2
    { shift && shift; } || return
  done
}

arr='{"a": "aa", "l": "bb", "c": "ccccc"}'
json_dict arr
declare -p arr
瞎闹 2025-01-19 07:17:26

仅使用 declare 并让 jq 使用 @sh 创建内容以实现 shell 一致性不是更简单吗?

索引数组:

unset arr; arr='["a", "b", "c"]'
declare -a arr="($(jq -r @sh <<< "$arr"))"

关联数组:

unset arr; arr='{"a": "aa", "l": "bb", "c": "ccccc"}'
declare -A arr="($(jq -r 'to_entries[] | @sh "[\(.key)]=\(.value)"' <<< "$arr"))"

响应编辑请求: 上面要求值是字符串(数字和布尔值也可以),但其他结构需要首先转换为字符串格式。特别是,在请求之后,@json 可用于将任意 JSON 内容编码为字符串。但是,请记住,这样做时,bash 数组的项目将进行 JSON 编码,这意味着简单的情况(上面的 aa 之类的字符串)也将被编码(例如 “aa”,包括引号,按照 JSON 的要求)。如果这是您想要的,请尝试:

具有 JSON 编码值的索引数组:

unset arr; arr='["a", {}, [null, {"a": true}]]'
declare -a arr="($(jq -r 'map(@json) | @sh' <<< "$arr"))"

具有 JSON 编码值的关联数组:

unset arr; arr='{"a": "aa", "b": {"l": "bb", "c": "ccccc"}}'
declare -A arr="($(jq -r 'to_entries[] | @sh "[\(.key)]=\(.value | @json)"' <<< "$arr"))"

Wouldn't it be simpler to just use declare and have jq create the contents using @sh for shell conformity?

Indexed array:

unset arr; arr='["a", "b", "c"]'
declare -a arr="($(jq -r @sh <<< "$arr"))"

Associative array:

unset arr; arr='{"a": "aa", "l": "bb", "c": "ccccc"}'
declare -A arr="($(jq -r 'to_entries[] | @sh "[\(.key)]=\(.value)"' <<< "$arr"))"

Reacting to an edit request: The above requires the values to be strings (numbers and booleans will kind of work, too), but other structures need to be brought into a stringy format first. In particular, following the request, @json could be used to encode an arbitrary JSON content as a string. However, keep in mind that in doing so, the bash array's items will be JSON-encoded, which means that also the simple cases (strings like aa from above) will be encoded (e.g. as "aa", including the quotes, as required by JSON). If this is what you want, go for it:

Indexed array with JSON-encoded values:

unset arr; arr='["a", {}, [null, {"a": true}]]'
declare -a arr="($(jq -r 'map(@json) | @sh' <<< "$arr"))"

Associative array with JSON-encoded values:

unset arr; arr='{"a": "aa", "b": {"l": "bb", "c": "ccccc"}}'
declare -A arr="($(jq -r 'to_entries[] | @sh "[\(.key)]=\(.value | @json)"' <<< "$arr"))"
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文