shell如何通过sed提取文件中的多个变量

zengzsys  于 2023-10-23  发布在  Shell
关注(0)|答案(3)|浏览(129)

我有一个项目,需要渲染一个模板。我想提前确保模板中的所有变量都不为空。
我能够通过以下脚本提取具有单行数据的模板,其中仅包含一个变量

  • 模板文件:tmpl_single.tmpl
{
  "var1": "${VARIABLE_1}",
  "var2": "${VARIABLE_2}",
  "var3": "${VARIABLE_3}"
}
  • shell脚本文件:generate.sh
#!/bin/bash
DIR_BASE="$(cd "$(dirname "$0")" && pwd)"
render_2_file() {
  template_file=$1
  out_file=$2
  set +u
  for env in $(sed -n 's/^[^#].*${\(.*\)}.*/\1/p' $template_file); do
    # debug
    echo "$env : $(eval echo \$${env})"
    if [ -z "$(eval echo \$${env})" ]; then
      echo "environment variable '${env}' not set"
      missing=true
    fi
  done
  if [ "${missing}" ]; then
    echo 'Please check the above variable'
    exit 1
  fi
  eval "cat << EOF
$(cat ${template_file})
EOF" >"$out_file"
  set -u
}
main(){
    # debug generate 
    # VARIABLE_1=var1_val
    # VARIABLE_2=var1_val
    # VARIABLE_3=var1_val

    # single var in one line 
    TMPL_PATH=$DIR_BASE/tmpl_single.tmpl
    OUT_FILE=$DIR_BASE/tmpl_single.json
    # multi var in one line 
    # TMPL_PATH=$DIR_BASE/tmpl_multi.tmpl
    # OUT_FILE=$DIR_BASE/tmpl_multi.json
    echo "check path = $OUT_FILE"
    if [ ! -f "$OUT_FILE" ]; then
        echo "not found, generating"
        render_2_file "$TMPL_PATH" "$OUT_FILE"
        if [ $? = 0 ]; then
            echo "generate successfully"
        fi
    else
        echo "out file existed, no need to generate"
    fi
}
main "$@"

输出结果如下,它可以检测到单行一个变量的情况,此时输出错误,没有生成目标文件

stdout:
not found, generating
VARIABLE_1 : 
environment variable 'VARIABLE_1' not set
VARIABLE_2 : 
environment variable 'VARIABLE_2' not set
VARIABLE_3 : 
environment variable 'VARIABLE_3' not set
Please check the above variable

但是,如果模板文件中的一行包含多个变量,则只能提取该行中的最后一个变量。

  • 模板文件:tmpl_multi.tmpl
{
  "var12": "test1_${VARIABLE_1}:test2_${VARIABLE_2}",
  "var3": "test3_${VARIABLE_3}"
}
  • 标准输出:
not found, generating
VARIABLE_2 : 
environment variable 'VARIABLE_2' not set
VARIABLE_3 : 
environment variable 'VARIABLE_3' not set
Please check the above variable

从上面的输出可以看出,变量VARIABLE_1没有被提取。
请告诉我如何提取一行数据中${} Package 的多个变量期待您的回复。

mzsu5hc0

mzsu5hc01#

我会谨慎使用eval。想象一下,用${PATH+$(rm -rf /*)}或一个变量检查一个输入文件,这个变量会扩展成一些可疑的东西。
然而,如果你想走这条路,grep是从行中提取多个匹配的完美选择:

grep -io '\${[a-z0-9_]*}' | grep -o '[^${}]*' | sort -u

但是,由于您的输入似乎已经是有效的JSON,并且解释为变量的“规则”是有限的,我可以让您对jq感兴趣吗?

empty="$(jq -re 'map(scan("\\${(\\w+)}")[] | select(env[.]|length==0)) | unique[]' vars.json)" \
  && echo "The following vars are empty: $empty";

if empty="$(jq -re 'map(scan("\\${(\\w+)}")[] | select(env[.]|length==0)) | unique[]' vars.json)"; then
  echo "The following vars are empty: $empty";
fi

empty="$(jq -re 'map(scan("\\${(\\w+)}")[] | select(env[.]|length==0)) | unique[]' vars.json)";
if [ "$empty" ]; then
  echo "The following vars are empty: $empty";
fi
ma8fv8wu

ma8fv8wu2#

您可以首先使用grep -oP提取变量名并将其保存到bash数组中。
然后检查它们是否都存在于文件中。
然后你**export**它们,并调用一个像envsubst这样的程序来处理模板。

**注意:**您仍然需要确保添加的内容不会破坏您的JSON。

#!/bin/bash

readarray -t varnames < <(
    LANG=C grep -oP '(?<=\$\{)[[:alpha:]_][[:alnum:]_]*(?=})' tmpl_single.tmpl |
    sort -u
)

for ref in "${varnames[@]}"
do
    [[ ${!ref:+1} ]] || {
        printf 'environment variable %s is not defined\n' "$ref" >&2
        exit 1
    }
done

(
    export "${varnames[@]}"
    envsubst < tmpl_single.tmpl
)
3qpi33ja

3qpi33ja3#

使用Bash + TXR

$ ./check.sh tmpl_single.tmpl
variable VARIABLE_1 doesn't exist
variable VARIABLE_3 doesn't exist

check.sh中的代码:

#!/bin/bash

VARIABLE_2=abc   # make VARIABLE_2 exist, for testing

eval $(txr -B -c \
'@(collect)
@(coll :vars (VAR))${@{VAR /[_a-zA-Z0-9]+/}}@(end)
@(end)
@(flatten VAR)' "$@")

for V in ${VAR[@]}; do
  if [ -z "${!V+x}" ]; then
    printf "variable %s doesn't exist\n" $V
  fi
done

Txr -B以shell赋值格式输出变量绑定,并进行适当的转义。列表变量变成数组赋值,如VAR[0]=VARIABLE_1,这将使我们获得Bash数组中的变量。
一个改进是删除重复项,因为变量可能在模板中出现不止一次。

相关问题