yq 基于现有文件创建复杂对象
yq create complex object based on existing file
给定一个 docker 撰写文件:
version: '3'
services:
service1:
image: image:name
environment:
- TEMP_ID=1928
volumes:
- type: bind
source: local/path/to
target: container/path/to
ports:
- 8900:8580
service2:
image: image:name
environment:
- TEMP_ID=1451
volumes:
- type: bind
source: local/path/to/1451
target: container/path/to
ports:
- 8901:8580
我仅限于编写一个bash 脚本来根据其内容向上述模板添加服务。一些值是直接从写在服务数组中的最后一个服务复制的,一些字段需要修改。
我设法提取并准备了我需要添加的所有值,但我一直坚持创建服务对象并将其添加到现有文件中。
到目前为止我的脚本是:
#!/bin/bash
SERVICE_NAME=
TEMP_ID_ARG=
#extract data to copy from last configured client.
SERVICES=($(yq eval '.services | keys' docker-compose.yml))
LAST_SERVICE=${SERVICES[${#SERVICES[@]}-1]}
echo "adding user based on last service: $LAST_SERVICE"
IMAGE=$(yq -P -C eval '.services["'$LAST_SERVICE'"].image' docker-compose.yml)
ENVIRONEMNT_ENTRY="TEMP_ID=${TEMP_ID_ARG}"
TARGET_PATH_IN_CONTAINER=$(yq -P -C eval '.services["'$LAST_SERVICE'"].volumes[0].target' docker-compose.yml)
VOLUME_TYPE=$(yq -P -C eval '.services["'$LAST_SERVICE'"].volumes[0].type' docker-compose.yml)
LOCAL_PATH_TO_SOURCES=$(yq -P -C eval '.services["'$LAST_SERVICE'"].volumes[0].source' docker-compose.yml)
PATH_AS_ARRAY=($(echo $LOCAL_PATH_TO_SOURCES | tr "\/" " "))
PATH_AS_ARRAY[${#PATH_AS_ARRAY[@]}-1]=$TEMP_ID_ARG
NEW_PATH_TO_RESOURCE=$(printf "/%s" "${PATH_AS_ARRAY[@]}")
# extract port mapping, take first argument (exposed port) increment its value by 1 (no upper limitation)
# join back together with : delimiter.
PORT_MAPING_AS_ARRAY=($(yq -P -C eval '.services["'$LAST_SERVICE'"].ports[0]' docker-compose.yml | tr ":" " "))
# NO UPPER LIMITATION FOR PORT!!!
PORT_MAPING_AS_ARRAY[0]=$(expr $PORT_MAPING_AS_ARRAY + 1)
NEW_PORT_MAPPING=$(printf ":%s" "${PORT_MAPING_AS_ARRAY[@]}")
NEW_PORT_MAPPING=${NEW_PORT_MAPPING:1}
VOLUME_ENTRY=$(yq -P -C eval --null-input '.type = "'$VOLUME_TYPE'" | .source = "'$NEW_PATH_TO_RESOURCE'" | .target = "'$TARGET_PATH_IN_CONTAINER'"')
test=$(yq -P -C -v eval --null-input ' .'$SERVICE_NAME'.image = "'$IMAGE'" | .'$SERVICE_NAME'.environement = ["'$ENVIRONEMNT_ENTRY'"] | (.'$SERVICE_NAME'.volumes = ["'$VOLUME_ENTRY'"] | .'$SERVICE_NAME'.ports = ["'NEW_PORT_MAPPING'"])')
echo $test
当运行时,我认为是所有部分的工作装配命令,returns出现以下错误:
Error: cannot pass files in when using null-input flag
调用时的预期输出 add_service.sh service3 1234
给定上述输入文件:
version: '3'
services:
service1:
image: image:name
environment:
- TEMP_ID=1928
volumes:
- type: bind
source: local/path/to
target: container/path/to
ports:
- 8900:8580
service2:
image: image:name
environment:
- TEMP_ID=1451
volumes:
- type: bind
source: local/path/to/1451
target: container/path/to
ports:
- 8901:8580
service3:
image: image:name
environment:
- TEMP_ID=1234
volumes:
- type: bind
source: local/path/to/1234
target: container/path/to
ports:
- 8902:8580
由于我的 bash 技能不是那么强,我欢迎任何建议或更好的解决方案来解决我的问题。
您应该通过一次 yq 调用完成所有操作,将外部值作为变量传递;这将使代码更安全和更快:
service_name="service3" \
temp_id="1234" \
environment="TEMP_ID=1234" \
yq eval '
.services[ .services | keys | .[-1] ] as $last |
.services[ strenv(service_name) ] = {
"image": $last.image,
"environment": [ strenv(environment) ],
"volumes": [ {
"type": $last.volumes[0].type,
"source": (
$last.volumes[0].source |
split("/") |
.[-1] = strenv(temp_id) |
join("/")
),
"target": $last.volumes[0].target
} ],
"ports": [ (
"" + $last.ports[0] |
split(":") |
.[0] tag = "!!int" |
.[0] += 1 |
join(":") |
. tag = ""
) ]
}
' docker-compose.yml
输出:
version: '3'
services:
service1:
image: image:name
environment:
- TEMP_ID=1928
volumes:
- type: bind
source: local/path/to
target: container/path/to
ports:
- 8900:8580
service2:
image: image:name
environment:
- TEMP_ID=1451
volumes:
- type: bind
source: local/path/to/1451
target: container/path/to
ports:
- 8901:8580
service3:
image: image:name
environment:
- TEMP_ID=1234
volumes:
- type: bind
source: local/path/to/1234
target: container/path/to
ports:
- 8902:8580
给定一个 docker 撰写文件:
version: '3'
services:
service1:
image: image:name
environment:
- TEMP_ID=1928
volumes:
- type: bind
source: local/path/to
target: container/path/to
ports:
- 8900:8580
service2:
image: image:name
environment:
- TEMP_ID=1451
volumes:
- type: bind
source: local/path/to/1451
target: container/path/to
ports:
- 8901:8580
我仅限于编写一个bash 脚本来根据其内容向上述模板添加服务。一些值是直接从写在服务数组中的最后一个服务复制的,一些字段需要修改。 我设法提取并准备了我需要添加的所有值,但我一直坚持创建服务对象并将其添加到现有文件中。 到目前为止我的脚本是:
#!/bin/bash
SERVICE_NAME=
TEMP_ID_ARG=
#extract data to copy from last configured client.
SERVICES=($(yq eval '.services | keys' docker-compose.yml))
LAST_SERVICE=${SERVICES[${#SERVICES[@]}-1]}
echo "adding user based on last service: $LAST_SERVICE"
IMAGE=$(yq -P -C eval '.services["'$LAST_SERVICE'"].image' docker-compose.yml)
ENVIRONEMNT_ENTRY="TEMP_ID=${TEMP_ID_ARG}"
TARGET_PATH_IN_CONTAINER=$(yq -P -C eval '.services["'$LAST_SERVICE'"].volumes[0].target' docker-compose.yml)
VOLUME_TYPE=$(yq -P -C eval '.services["'$LAST_SERVICE'"].volumes[0].type' docker-compose.yml)
LOCAL_PATH_TO_SOURCES=$(yq -P -C eval '.services["'$LAST_SERVICE'"].volumes[0].source' docker-compose.yml)
PATH_AS_ARRAY=($(echo $LOCAL_PATH_TO_SOURCES | tr "\/" " "))
PATH_AS_ARRAY[${#PATH_AS_ARRAY[@]}-1]=$TEMP_ID_ARG
NEW_PATH_TO_RESOURCE=$(printf "/%s" "${PATH_AS_ARRAY[@]}")
# extract port mapping, take first argument (exposed port) increment its value by 1 (no upper limitation)
# join back together with : delimiter.
PORT_MAPING_AS_ARRAY=($(yq -P -C eval '.services["'$LAST_SERVICE'"].ports[0]' docker-compose.yml | tr ":" " "))
# NO UPPER LIMITATION FOR PORT!!!
PORT_MAPING_AS_ARRAY[0]=$(expr $PORT_MAPING_AS_ARRAY + 1)
NEW_PORT_MAPPING=$(printf ":%s" "${PORT_MAPING_AS_ARRAY[@]}")
NEW_PORT_MAPPING=${NEW_PORT_MAPPING:1}
VOLUME_ENTRY=$(yq -P -C eval --null-input '.type = "'$VOLUME_TYPE'" | .source = "'$NEW_PATH_TO_RESOURCE'" | .target = "'$TARGET_PATH_IN_CONTAINER'"')
test=$(yq -P -C -v eval --null-input ' .'$SERVICE_NAME'.image = "'$IMAGE'" | .'$SERVICE_NAME'.environement = ["'$ENVIRONEMNT_ENTRY'"] | (.'$SERVICE_NAME'.volumes = ["'$VOLUME_ENTRY'"] | .'$SERVICE_NAME'.ports = ["'NEW_PORT_MAPPING'"])')
echo $test
当运行时,我认为是所有部分的工作装配命令,returns出现以下错误:
Error: cannot pass files in when using null-input flag
调用时的预期输出 add_service.sh service3 1234
给定上述输入文件:
version: '3'
services:
service1:
image: image:name
environment:
- TEMP_ID=1928
volumes:
- type: bind
source: local/path/to
target: container/path/to
ports:
- 8900:8580
service2:
image: image:name
environment:
- TEMP_ID=1451
volumes:
- type: bind
source: local/path/to/1451
target: container/path/to
ports:
- 8901:8580
service3:
image: image:name
environment:
- TEMP_ID=1234
volumes:
- type: bind
source: local/path/to/1234
target: container/path/to
ports:
- 8902:8580
由于我的 bash 技能不是那么强,我欢迎任何建议或更好的解决方案来解决我的问题。
您应该通过一次 yq 调用完成所有操作,将外部值作为变量传递;这将使代码更安全和更快:
service_name="service3" \
temp_id="1234" \
environment="TEMP_ID=1234" \
yq eval '
.services[ .services | keys | .[-1] ] as $last |
.services[ strenv(service_name) ] = {
"image": $last.image,
"environment": [ strenv(environment) ],
"volumes": [ {
"type": $last.volumes[0].type,
"source": (
$last.volumes[0].source |
split("/") |
.[-1] = strenv(temp_id) |
join("/")
),
"target": $last.volumes[0].target
} ],
"ports": [ (
"" + $last.ports[0] |
split(":") |
.[0] tag = "!!int" |
.[0] += 1 |
join(":") |
. tag = ""
) ]
}
' docker-compose.yml
输出:
version: '3'
services:
service1:
image: image:name
environment:
- TEMP_ID=1928
volumes:
- type: bind
source: local/path/to
target: container/path/to
ports:
- 8900:8580
service2:
image: image:name
environment:
- TEMP_ID=1451
volumes:
- type: bind
source: local/path/to/1451
target: container/path/to
ports:
- 8901:8580
service3:
image: image:name
environment:
- TEMP_ID=1234
volumes:
- type: bind
source: local/path/to/1234
target: container/path/to
ports:
- 8902:8580