我想通过`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`声明呢?
是的,对于许多类型的JSON文档(尽管不是所有类型),有一种更简洁的方法,即here文档:
对于这种用例,我认为here文档是一种双引号字符串的形式,因为它在文档中展开shell/环境变量,但它不会删除双引号字符,因为它们不是字符串定界符。
当您的JSON文档较小时,您可以使用内置的
printf
命令在文档中间插入值,并将生成的JSON保存在shell变量中:{
`和`[
`等)和数据(函数的参数)的混合。我会将数据与格式分开,并使用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`之前随意调整格式,而不影响数据(反之亦然),因为我们已经将数据与格式解耦。