使用Shell脚本读取和替换键值属性



我想用相应的值替换模板文件中的键(使用find递归地找到)。密钥将从属性文件加载。请看下面的脚本:

脚本文件目录:

. /home/uname/config/appl.properties
echo ${name_1}
echo ${age_1}
job_1="IT"
echo ${job_1}
for file in $(find /home/uname/config -name "*.txt"); do
  echo $file
  temp_file="/home/uname/temp_file.txt"
  cp -f $file $temp_file
  rm $file
  envsubst < $temp_file > $file
done

:。属性内容:

name_1="theBestName"
age_1="25"

template.txt内容:

My name is ${name_1}, my age is ${age_1} and my job is in ${job_1}.

我能够加载键/值对(echo语句显示键值),但是当envsubst写出模板文件时,它没有用值替换任何键。我得到的输出是:

My name is  and my age is and my job is in .

envsubst,顾名思义,要求它的键/值对在环境变量中。仅仅赋值创建shell变量,而不是环境变量。

以下是对最佳实践替代的尝试:

set -a # turn on auto-export
. appl.properties
set -a # turn off auto-export
while IFS= read -r -d '' filename; do
  tempfile=$(mktemp "$filename.XXXXXX")
  if envsubst <"$filename" >"$tempfile"; then
    mv "$tempfile" "$filename"
  else
    rm -f "$tempfile"
  fi
done < <(find /home/uname/config -name '*.txt' -print0)

重点:

  • 引号很重要。试着在文件名中加入空格,如果你不太确定,看看原来的行为。
  • 在获取文件之前使用set -a确保它设置的变量存在于环境中。
  • 使用IFS= read -r -d '' filename从一个空分隔的源(如find -print0)迭代的名字-不像for filename in $(find ...) -确保你的代码正确处理文件名与空格,文件名与glob字符的名字,和其他不寻常的情况。
  • 使用while read ...; done < <(find ...)而不是find ... | while read可以避免BashFAQ #24中记录的错误,即在循环中设置的变量不会持久存在。有关通过此方法逐行读取流的更多信息,请参阅BashFAQ #1和/或UsingFind页面。
  • 使用mktemp生成临时文件名可以防止符号链接攻击——想象一下,如果其他人有权限写/home/uname/,但是没有权限写/etc,创建了一个指向/etc/passwd(或任何其他您关心的文件)的符号链接/home/uname/temp_file.txt,会发生什么。此外,使用mktemp生成随机名称意味着同一脚本的多个并发实例不会踩踏相同的临时文件名。
  • envsubst的输出写入临时文件,然后将该临时文件重命名为您的目标名称,确保在完成生成之前不会覆盖输出。因此,即使进程在部分完成时中断,您也可以在某种程度上保证输出文件将保持其原始状态或完全写入(详细信息取决于您的文件系统的配置和语义)。

> $fileenvsubst开始前截断文件。使用moreutils的海绵(1)来解决这个问题,或者编写自己的海绵,它将在输出之前捕获所有输入。

最新更新