使用 shell 脚本拆分字符串并提取变量
Split string and extract variables with shell script
问题
给出这个单行字符串:
PG_USER=postgres PG_PORT=1234 PG_PASS=icontain=and*symbols
将每个值分配给指定变量以便我以后可以使用它的正确方法是什么?
上下文
我正在 CronJob
中解析 k8s 秘密的上下文,以便我可以定期调用 Postgres 数据库中的存储过程。
为此,我计划使用:
PG_OUTPUT_VALUE=$(PGPASSWORD=$PG_PASSWD psql -qtAX -h $PG_HOST -p $PG_PORT -U $PG_USER -d $PG_DATABASE -c $PG_TR_CLEANUP_QUERY)
echo $PG_OUTPUT_VALUE
我目前正在尝试修复的实际整个舵图如下所示:
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: {{ template "fullname" $ }}-tr-cleanup-cronjob
spec:
concurrencyPolicy: Forbid
schedule: "* * * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
volumes:
- name: postgres
secret:
secretName: {{ template "fullname" $ }}-postgres
containers:
- name: {{ template "fullname" $ }}-tr-cleanup-pod
image: postgres:12-alpine
imagePullPolicy: Always
env:
- name: PG_PROPS
valueFrom:
secretKeyRef:
name: {{ template "fullname" $ }}-postgres
key: postgres.properties
command:
- /bin/sh
- -c
- echo "props:" && echo $PG_PROPS && PG_USER=$(grep "^PG_USER=" | cut -d"=" -f2-) && echo $PG_USER && PG_TR_CLEANUP_QUERY="SELECT something FROM public.somewhere;" && echo $PG_TR_CLEANUP_QUERY && PG_OUTPUT_VALUE=$(PGPASSWORD=$PG_PASSWD psql -qtAX -h $PG_HOST -p $PG_PORT -U $PG_USER -d $PG_DATABASE -c $PG_TR_CLEANUP_QUERY) && echo PG_OUTPUT_VALUE
volumeMounts:
- name: postgres
mountPath: /etc/secrets/postgres
当前方法
如您所见,我目前正在使用:
PG_USER=$(grep "^PG_USER=" | cut -d"=" -f2-)
那是因为我一开始以为secret会分多行输出,结果证明我错了。 echo $PG_USER
显示空字符串。
选项 1
可以重复使用此函数来单独分配每个变量:
extract() {
echo "$INPUT" | grep -o "=.*" | cut -d" " -f1 | cut -d"=" -f2- ;
}
并使用它:
PG_USER=$(extract PG_USER)
PG_PORT=$(extract PG_PORT)
PG_PASS=$(extract PG_PASS)
选项 2
另一个可能的解决方案,存在安全问题,就是简单地使用:
eval "$INPUT"
只有在您验证输入后才应使用它。
上下文完整答案
因为我已经在问题中介绍了 k8s 上下文,所以这里是插入该解决方案的答案。
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: {{ template "fullname" $ }}-cronjob
spec:
concurrencyPolicy: Forbid
schedule: "* * * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
volumes:
- name: postgres
secret:
secretName: {{ template "fullname" $ }}-postgres
containers:
- name: {{ template "fullname" $ }}-cronjob-pod
image: postgres:12-alpine
imagePullPolicy: Always
env:
- name: PG_PROPS
valueFrom:
secretKeyRef:
name: {{ template "fullname" $ }}-postgres
key: postgres.properties
command:
- /bin/sh
- -c
- >-
extract() { echo "$PG_PROPS" | grep -o "=.*" | cut -d" " -f1 | cut -d"=" -f2- ; } &&
export PGHOST=$(extract PG_HOST) &&
export PGPORT=$(extract PG_PORT) &&
export PGDATABASE=$(extract PG_DATABASE) &&
export PGUSER=$(extract PG_USER) &&
PG_SCHEMA=$(extract PG_SCHEMA) &&
PG_QUERY="SELECT tenant_schema FROM $PG_SCHEMA.tenant_schema_mappings;" &&
PGPASSWORD=$(extract PG_PASSWD) psql --echo-all -c "$PG_QUERY"
volumeMounts:
- name: postgres
mountPath: /etc/secrets/postgres
bashdeclare
命令在这里很合适,比eval
.
更安全
假设输入包含潜在的恶意内容
line='PG_USER=postgres PG_PORT=1234 PG_PASS=icontain=and*symbols`ls`'
我假设 none 个值包含空格。让我们拆分那个字符串
read -ra assignments <<< "$line"
现在,declare
每一个
for assignment in "${assignments[@]}"; do declare "$assignment"; done
我们在检查输入的任何地方都维护双引号。
让我们看看我们最终得到了什么:
$ declare -p PG_USER PG_PORT PG_PASS
declare -- PG_USER="postgres"
declare -- PG_PORT="1234"
declare -- PG_PASS="icontain=and*symbols\`ls\`"
问题
给出这个单行字符串:
PG_USER=postgres PG_PORT=1234 PG_PASS=icontain=and*symbols
将每个值分配给指定变量以便我以后可以使用它的正确方法是什么?
上下文
我正在 CronJob
中解析 k8s 秘密的上下文,以便我可以定期调用 Postgres 数据库中的存储过程。
为此,我计划使用:
PG_OUTPUT_VALUE=$(PGPASSWORD=$PG_PASSWD psql -qtAX -h $PG_HOST -p $PG_PORT -U $PG_USER -d $PG_DATABASE -c $PG_TR_CLEANUP_QUERY)
echo $PG_OUTPUT_VALUE
我目前正在尝试修复的实际整个舵图如下所示:
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: {{ template "fullname" $ }}-tr-cleanup-cronjob
spec:
concurrencyPolicy: Forbid
schedule: "* * * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
volumes:
- name: postgres
secret:
secretName: {{ template "fullname" $ }}-postgres
containers:
- name: {{ template "fullname" $ }}-tr-cleanup-pod
image: postgres:12-alpine
imagePullPolicy: Always
env:
- name: PG_PROPS
valueFrom:
secretKeyRef:
name: {{ template "fullname" $ }}-postgres
key: postgres.properties
command:
- /bin/sh
- -c
- echo "props:" && echo $PG_PROPS && PG_USER=$(grep "^PG_USER=" | cut -d"=" -f2-) && echo $PG_USER && PG_TR_CLEANUP_QUERY="SELECT something FROM public.somewhere;" && echo $PG_TR_CLEANUP_QUERY && PG_OUTPUT_VALUE=$(PGPASSWORD=$PG_PASSWD psql -qtAX -h $PG_HOST -p $PG_PORT -U $PG_USER -d $PG_DATABASE -c $PG_TR_CLEANUP_QUERY) && echo PG_OUTPUT_VALUE
volumeMounts:
- name: postgres
mountPath: /etc/secrets/postgres
当前方法
如您所见,我目前正在使用:
PG_USER=$(grep "^PG_USER=" | cut -d"=" -f2-)
那是因为我一开始以为secret会分多行输出,结果证明我错了。 echo $PG_USER
显示空字符串。
选项 1
可以重复使用此函数来单独分配每个变量:
extract() {
echo "$INPUT" | grep -o "=.*" | cut -d" " -f1 | cut -d"=" -f2- ;
}
并使用它:
PG_USER=$(extract PG_USER)
PG_PORT=$(extract PG_PORT)
PG_PASS=$(extract PG_PASS)
选项 2
另一个可能的解决方案,存在安全问题,就是简单地使用:
eval "$INPUT"
只有在您验证输入后才应使用它。
上下文完整答案
因为我已经在问题中介绍了 k8s 上下文,所以这里是插入该解决方案的答案。
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: {{ template "fullname" $ }}-cronjob
spec:
concurrencyPolicy: Forbid
schedule: "* * * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
volumes:
- name: postgres
secret:
secretName: {{ template "fullname" $ }}-postgres
containers:
- name: {{ template "fullname" $ }}-cronjob-pod
image: postgres:12-alpine
imagePullPolicy: Always
env:
- name: PG_PROPS
valueFrom:
secretKeyRef:
name: {{ template "fullname" $ }}-postgres
key: postgres.properties
command:
- /bin/sh
- -c
- >-
extract() { echo "$PG_PROPS" | grep -o "=.*" | cut -d" " -f1 | cut -d"=" -f2- ; } &&
export PGHOST=$(extract PG_HOST) &&
export PGPORT=$(extract PG_PORT) &&
export PGDATABASE=$(extract PG_DATABASE) &&
export PGUSER=$(extract PG_USER) &&
PG_SCHEMA=$(extract PG_SCHEMA) &&
PG_QUERY="SELECT tenant_schema FROM $PG_SCHEMA.tenant_schema_mappings;" &&
PGPASSWORD=$(extract PG_PASSWD) psql --echo-all -c "$PG_QUERY"
volumeMounts:
- name: postgres
mountPath: /etc/secrets/postgres
bashdeclare
命令在这里很合适,比eval
.
假设输入包含潜在的恶意内容
line='PG_USER=postgres PG_PORT=1234 PG_PASS=icontain=and*symbols`ls`'
我假设 none 个值包含空格。让我们拆分那个字符串
read -ra assignments <<< "$line"
现在,declare
每一个
for assignment in "${assignments[@]}"; do declare "$assignment"; done
我们在检查输入的任何地方都维护双引号。
让我们看看我们最终得到了什么:
$ declare -p PG_USER PG_PORT PG_PASS
declare -- PG_USER="postgres"
declare -- PG_PORT="1234"
declare -- PG_PASS="icontain=and*symbols\`ls\`"