所以我在 bash 脚本中定义了以下变量:
new_commit="back:h3912kk"
old_commit="back:1.0.1"
file = docker-compose.yml
然后我有这个 yml $file
:
version: '3'
services:
mongo:
container_name: mongo
image: mongo:3.4
volumes:
- /home/mongo_files:/data/db
networks:
my_network:
back:
container_name: back
image: index.docker.io/gg/back:1.0.1
networks:
my_network
networks:
my_network:
我想更换线路
image: index.docker.io/gg/back:1.0.1
为了
image: index.docker.io/gg/back:h3912kk
sed
我在 bash 脚本中使用以下命令:
echo sed -i "s/$old_commit/$new_commit/" $file
但是出现以下错误:
sed: -e expression #1, char 12: unterminated `s' command
当然 docker-compose 文件不会更新。
我究竟做错了什么?
如果我执行 echo 命令,则会显示:
sed -i s/back:1.0.1
/back:h3912kk/ docker-compose.yml
为什么 echo 不将命令显示在同一行上?
编辑:
正如@Kusalananda 所说,old_commit 变量不是静态文件,这就是我猜为什么我无法重现您提供的相关答案的主要原因$old_commit
(我虽然不会假设有问题)
为了获取old_commit
参数,我使用以下命令在 yml 文件中查找满足我感兴趣的条件的行,这是我的完整代码:
output='back:h3912kk'
# here I split the name of the repo and the commit by ':'
readarray -d ':' -t array_new_commit <<< "$output"
repo="${array_new_commit[0]}"
file=docker-compose.yml
# here I loop over the lines of the yml file:
while read -r line; do
# if a line contains the repo name str and image str I generate a line with the new commit
if [[ $line == *$repo* && $line == *"image"* ]]; then
# old_line=$line
readarray -d '/' -t array_old_commit <<< "$line"
old_commit=${array_old_commit[-1]}
# new_line="${array_old_commit[0]}:${array_old_commit[1]}:$new_commit"
fi
done<"$file"
不知何故(不知道为什么,我是 bash 的新手)以这种方式生成这个变量实际上不允许我像静态变量一样使用它。 (我真的很想明白这一点)
您能否给我一个解决方法,将变量替换old_commit
为new_commit
yml 文件中的变量?
再次感谢 Kusalananda 指出这个问题
PD 我正在使用 ubuntu 0.18.4 发行版
编辑: 使用第二段代码:
yq -Y --arg new "$new_commit" --arg reposit "$repo" \
'".services." + $reposit + ".image |=index.docker.io/gg/" + $new' docker-compose.yml
我的输出是:
.services.back.image |=index.docker.io/gg/back:h3912kk
...
答案1
您不应该使用sed
YAML 文件或以任何结构化文档格式(YAML、JSON、XML 等)编写的文件,而是使用为该文档格式编写的解析器。
对于 YAML,有两个为 shell 编写的好工具,都被容易混淆地称为yq
.来自yq
https://kislyuk.github.io/yq/是一个易于使用的jq
包装器(并且jq
是一个 JSON 解析器)。
对于这个答案的一部分,我假设您已经定义了两个 shell 变量old_commit
,并且new_commit
与问题中的代码中显示的完全一样。您稍后显示的输出告诉我您实际上并未将它们分配为静态字符串,而是通过其他一些您没有提及的过程(其中$old_commit
似乎获得了尾随换行符),所以我不能说太多并会忽略它。
要使用此实现更改、 under 下image
键的值,您可以使用back
services
yq
yq -Y '.services.back.image |= "index.docker.io/gg/back:h3912kk"' file.yml
或者,使用您的$new_commit
值:
new_commit='back:h3912kk'
yq -Y --arg new "$new_commit" \
'.services.back.image |=
"index.docker.io/gg/" + $new' file.yml
请注意,我们进口--arg
使用创建yq
变量将 shell 变量的值添加到表达式中$new
,而不是直接将其注入到表达式字符串中。这样我们就可以确保该值被正确编码yq
,并且还可以确保我们的代码中没有任何代码注入漏洞。
或者,不必提及字符串index.docker.io/gg/
:
new_commit='back:h3912kk'
yq -Y --arg new "$new_commit" \
'.services.back.image |=
sub("[^/]*$"; "") + $new' file.yml
或者,为存储库使用单独的变量:
new_commit='back:h3912kk'
yq -Y --arg new "$new_commit" --arg repo 'back' \
'.services[$repo].image |=
sub("[^/]*$"; "") + $new' file.yml
我们还可以使用$old_commit
字符串来查找要替换的正确图像,而不是使用静态路径:
old_commit='back:1.0.1'
new_commit='back:h3912kk'
yq -Y --arg old "$old_commit" --arg new "$new_commit" \
'(.services[].image | select(endswith($old))) |=
(rtrimstr($old) + $new)' file.yml
这里我们选择全部 .services.*.image
以 中的任何字符串结尾的路径$old_commit
,然后我们将其从末尾修剪掉并将其替换为$new_commit
。
考虑到问题中的示例 YAML 文档,上面的每个命令都会生成
version: '3'
services:
mongo:
container_name: mongo
image: mongo:3.4
volumes:
- /home/mongo_files:/data/db
networks:
my_network: null
back:
container_name: back
image: index.docker.io/gg/back:h3912kk
networks: my_network
networks:
my_network: null
您可以轻松地将输出重定向到新文件(或使用 就地编辑 YAML 文档--in-place
)。
yq
你从 Ubuntu 等获得的工具是snap
不同的,我倾向于使用它来将 YAML 转换为 JSON(然后再转换回来):
yq -j e file.yml |
jq '.services.back.image |= "index.docker.io/gg/back:h3912kk"' |
yq -P e
给定示例 YAML 文件,这会产生与上面相同的输出。上面的表达式jq
可以替换为该答案顶部的任何表达式,具有相同的语义。
答案2
我不会评判你在 yaml 文件上使用 sed。每个人都用他想要的方式来完成工作。
我前段时间遇到了同样的问题,似乎如果你在 sed 正则表达式中“支撑”你的变量,整个事情就会开始起作用:
$ cat $file
version: '3'
services:
mongo:
container_name: mongo
image: mongo:3.4
volumes:
- /home/mongo_files:/data/db
networks:
my_network:
back:
container_name: back
image: index.docker.io/gg/back:1.0.1
networks:
my_network
networks:
my_network:
然后 :
$ sed s/${old_commit}/${new_commit}/ $file
给出这个结果:
version: '3'
services:
mongo:
container_name: mongo
image: mongo:3.4
volumes:
- /home/mongo_files:/data/db
networks:
my_network:
back:
container_name: back
image: index.docker.io/gg/back:h3912kk
networks:
my_network
networks:
my_network:
希望这可以帮助..
在 MacOS 10.13(bash 5.1.4.1、gnu sed 4.8)和 Ubuntu 16.04(bash 4.3.48、gnu sed 4.2.2)上测试
答案3
yq -i -Y --arg new "$VERSION" '.tool.image.tag |= $new' values.yaml