AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 776914
Accepted
Marc Le Bihan
Marc Le Bihan
Asked: 2024-05-22 13:37:27 +0800 CST2024-05-22 13:37:27 +0800 CST 2024-05-22 13:37:27 +0800 CST

是否有一种更简洁的方法将参数化的JSON字符串赋值给bash变量?

  • 772
我想通过`curl`的方式来为REST宠物商店服务器准备一个测试。 为了测试它的`添加新宠物`API,我准备了一个包含要插入的`Pet`的json字符串,在一个执行`curl`的bash函数中: ```bash #!/bin/bash # 添加新宠物 add_petstore() { local rest_url=$1 local id_pet=$2 local id_category=$3 local category_name=$4 local pet_name=$5 local photo_url=$6 local tag_id=$7 local tag_name=$8 local pet_to_add="{ \ \"id\": $id_pet, \ \"category\": { \ \"id\": $id_category, \ \"name\": \"$category_name\" \ }, \ \"name\": \"$pet_name\", \ \"photoUrls\": [ \ \"$photo_url\" \ ], \ \"tags\": [ \ { \"id\": $tag_id, \ \"name\": \"$tag_name\" \ } \ ] \ }"; echo "$pet_to_add" curl -X 'POST' \ "$rest_url" \ -H 'accept: application/xml' \ -H 'Content-Type: application/json' \ -d "$pet_to_add" } add_petstore "http://localhost:8080/pet" "151" "12" "Dogs" "REX" "http://photosofrex/rex_rage.jpg" "1" "ferocious sales" ``` `echo`的`$pet_to_add`看起来是我想要的: ```json { "id": 151, "category": { "id": 12, "name": "Dogs" }, "name": "REX", "photoUrls": [ "http://photosofrex/rex_rage.jpg" ], "tags": [ { "id": 1, "name": "ferocious sales" } ] } ``` `add_petstore`方法允许我轻松准备一些宠物。 但是`local pet_to_add=...`声明真的很乱。 如果有人(或我以后)需要修改这个脚本,这个局部变量并不友好。 我最初想到的是可以把内容放在一个文件中,然后用`local pet_to_add=$(cat myfile)`来读取。但这并不能解决它的变量参数问题。 我有没有一种更干净的方式来写`local pet_to_add`声明呢?
bash
  • 3 3 个回答
  • 809 Views

3 个回答

  • Voted
  1. Best Answer
    Sotto Voce
    2024-05-22T14:12:36+08:002024-05-22T14:12:36+08:00

    是的,对于许多类型的JSON文档(尽管不是所有类型),有一种更简洁的方法,即here文档:

    local pet_to_add=$(cat - <<END_DOC
      {
        "id": $id_pet,
        "category": {
          "id": $id_category,
          "name": "$category_name"
        },
        "name": "$pet_name",
        "photoUrls": [
          "$photo_url"
        ],
        "tags": [
          {
            "id": $tag_id,
            "name": "$tag_name"
          }
        ]
      }
    END_DOC
    )
    

    对于这种用例,我认为here文档是一种双引号字符串的形式,因为它在文档中展开shell/环境变量,但它不会删除双引号字符,因为它们不是字符串定界符。


    当您的JSON文档较小时,您可以使用内置的printf命令在文档中间插入值,并将生成的JSON保存在shell变量中:

    local example_json
    printf -v example_json '{ "name": "%s", "id": "%s" }' "${pet_name}" "${id_pet}"
    
    • 12
  2. Kusalananda
    2024-05-22T16:18:36+08:002024-05-22T16:18:36+08:00
    使用`jq`为顶级负载文档的每个子元素创建JSON片段,然后将它们全部组合在一起。这样,你确保`jq`有机会对每个字符串进行编码,并且你避免了将shell变量注入到JSON中(这可能会破坏文档;例如,见下面使用的`$pet_name`值)。 首先,你会设置带有它们值的shell变量。你可以使用从命令行读取的值。 ```bash pet_id=bee001 pet_name='Eric "the half a bee" II' category_id=bee category_name='The bees' # 我选择将标签作为关联数组来处理。 declare -A tags tags=( [beauty]=high [cost]=medium [social]=low ) # 我假设可以有很多照片URL。 photo_URLs=( "url1" "url2" "url3" ) ``` 然后我们为`category`部分创建JSON: ```bash # 创建类别 category_json=$( jq -c -n \ --arg id "$category_id" \ --arg name "$category_name" \ '$ARGS.named' ) ``` 这将创建一个带有`id`和`name`键的单个JSON对象。键的值来自给定的shell变量。`$ARGS.named`是一个内部的`jq`对象,包含使用`--arg`或`--argjson`(对于不应该或已经是字符串编码的值)在命令行上给出的所有键+值。由于`jq`表达式是单引的,shell不会误将`$ARGS`视为shell变量。 然后我们为`photoUrls`部分创建照片URL数组: ```bash # 创建照片URL数组 photoUrls_json=$( jq -c -n \ '$ARGS.positional' \ --args "${photo_URLs[@]}" ) ``` 这使用`jq`的`--args`,它用给定的值填充内部的`$ARGS.positional`数组。注意`--args`及其参数列表必须始终出现在命令行的最后。 接下来,我们从我们的关联数组创建`tags`部分: ```bash # 创建标签 tags_json=$( for tag_id in "${!tags[@]}"; do jq -c -n \ --arg id "$tag_id" \ --arg name "${tags[$tag_id]}" \ '$ARGS.named' done | jq -c -s '.' ) ``` 我们在一个循环中从关联shell数组的键和值创建数组元素,并使用`jq -s`从循环中读取将它们插入数组。 如果你的标签ID是数字,将`tags`作为普通的`bash`数组: ```bash tags=( [1]=high [12]=medium [90]=low ) ``` ……然后使用以下方式将其转换为JSON: ```bash # 创建标签 tags_json=$( for tag_id in "${!tags[@]}"; do jq -c -n \ --argjson id "$tag_id" \ --arg name "${tags[$tag_id]}" \ '$ARGS.named' done | jq -c -s '.' ) ``` 注意使用`--argjson id "$tag_id"`代替`--arg id "$tag_id"`来传递数字,它不应该被转换为字符串。 然后我们可以最终从这些部分组合JSON负载: ```bash # 创建最终的JSON负载 payload_json=$( jq -c -n \ --arg id "$pet_id" \ --argjson category "$category_json" \ --arg name "$pet_name" \ --argjson photoUrls "$photoUrls_json" \ --argjson tags "$tags_json" \ '$ARGS.named' ) # DEBUG: printf 'payload is\n%s\n' "$payload_json" ``` 上面的`bash`脚本将输出一个等同于以下内容的JSON文档(但以紧凑的形式): ```json { "id": "bee001", "category": { "id": "bee", "name": "The bees" }, "name": "Eric \"the half a bee\" II", "photoUrls": [ "url1", "url2", "url3" ], "tags": [ { "id": "beauty", "name": "high" }, { "id": "cost", "name": "medium" }, { "id": "social", "name": "low" } ] } ```
    • 8
  3. Ed Morton
    2024-05-22T20:06:04+08:002024-05-22T20:06:04+08:00
    你目前正在创建一个字符串,这个字符串是格式(字段名称、结构字符如`{`和`[`等)和数据(函数的参数)的混合。我会将数据与格式分开,并使用here文档来填充该格式,使用`-`在here文档开始分隔符中提供制表符缩进(在代码中填充字符串的位置的前导制表符不会出现在输出中),并将here文档分隔符用`'`s括起来,这样整个格式被视为单一引用字符串,以防止任何可能的shell扩展(例如`Cost $5`不会将`$5`扩展为位置参数),然后只需让`"$@"`展开以填充格式中的占位符`%s`s,这还有一个额外的好处,意味着你不需要所有那些其他的局部变量: ```bash $ cat tst.sh #!/usr/bin/env bash add_petstore() { local rest_url=$1 fmt pet_to_add shift IFS= read -r -d '' fmt <<<'!' { "id": %s, "category": { "id": %s, "name": "%s" }, "name": "%s", "photoUrls": [ "%s" ], "tags": [ { "id": %s, "name": "%s" } ] } ! printf -v pet_to_add "$fmt" "$@"; echo "$pet_to_add"; } add_petstore "http://localhost:8080/pet" "151" "12" "Dogs" "REX" "http://photosofrex/rex_rage.jpg" "1" "ferocious sales"; ``` ```json $ ./tst.sh { "id": 151, "category": { "id": 12, "name": "Dogs" }, "name": "REX", "photoUrls": [ "http://photosofrex/rex_rage.jpg" ], "tags": [ { "id": 1, "name": "ferocious sales" } ] } ``` here文档中的前导空格是制表符,因此由于`-`在`<<-`中,它们将被忽略。如果你确实想要输出中的制表符,那么将`<<-`更改为`<<`。 如果你确实不想要`pet_to_add`中的换行符,但确实想在填充格式字符串时使用它们,那么只需将此: ```bash printf -v pet_to_add "$fmt" "${@}"; ``` 更改为这样: ```bash printf -v pet_to_add "${fmt//$'\n'}" "${@}"; ``` 然后你会得到这样的输出: ```json $ ./tst.sh { "id": 151, "category": { "id": 12, "name": "Dogs" }, "name": "REX", "photoUrls": [ "http://photosofrex/rex_rage.jpg" ], "tags": [ { "id": 1, "name": "ferocious sales" } ]} ``` 如果你想将所有连续的空白字符压缩成单个空白字符,那么这样: ```bash shopt -s extglob printf -v pet_to_add "${fmt//+([[:space:]])/ }" "${@}"; ``` 会产生这样的输出: ```json $ ./tst.sh { "id": 151, "category": { "id": 12, "name": "Dogs" }, "name": "REX", "photoUrls": [ "http://photosofrex/rex_rage.jpg" ], "tags": [ { "id": 1, "name": "ferocious sales" } ] } ``` 关键是你可以在填充`pet_to_add`之前随意调整格式,而不影响数据(反之亦然),因为我们已经将数据与格式解耦。
    • 3

相关问题

  • 通过命令的标准输出以编程方式导出环境变量[重复]

  • 从文本文件传递变量的奇怪问题

  • 虽然行读取保持转义空间?

  • `tee` 和 `bash` 进程替换顺序

  • 运行一个非常慢的脚本直到它成功

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve