Answers:
使用您显示的输入类型,利用shell扩展将值替换为字符串的唯一方法eval
是以某种形式使用。这是安全的,只要您控制的值即可,str1
并且可以确保它仅引用被称为安全的变量(不包含机密数据)并且不包含任何其他未引用的shell特殊字符。您应该在双引号内或在here文档中扩展字符串,这种方式仅"$\`
是特殊的(它们必须\
在in 之前str1
)。
eval "substituted=\"$str1\""
定义一个函数而不是一个字符串会更加健壮。
fill_template () {
sentence1="I went to ${PLACE} and saw ${EVENT}"
sentence2="If you do ${ACTION} you will ${RESULT}"
}
设置变量,然后调用函数fill_template
设置输出变量。
PLACE=Sydney; EVENT=fireworks
ACTION='not learn from history'; RESULT='have to relive history'
fill_template
echo "During my holidays, $sentence1."
echo "Cicero said: \"$sentence2\"."
当我理解您的意思时,我认为这些答案都不正确。eval
完全没有必要,甚至不需要两次评估变量。
的确,@ Gilles非常接近,但是他没有解决可能覆盖值的问题,以及如果您多次需要它们,应该如何使用它们。毕竟,模板应该多次使用,对吗?
我认为,重要的是评估您的顺序。考虑以下:
在这里,您将设置一些默认值,并准备在调用时打印它们。
#!/bin/sh
_top_of_script_pr() (
IFS="$nl" ; set -f #only split at newlines and don't expand paths
printf %s\\n ${strings}
) 3<<-TEMPLATES
${nl=
}
${PLACE:="your mother's house"}
${EVENT:="the unspeakable."}
${ACTION:="heroin"}
${RESULT:="succeed."}
${strings:="
I went to ${PLACE} and saw ${EVENT}
If you do ${ACTION} you will ${RESULT}
"}
#END
TEMPLATES
在这里,您可以定义其他函数,根据它们的结果来调用打印函数...
EVENT="Disney on Ice."
_more_important_function() { #...some logic...
[ $((1+one)) -ne 2 ] && ACTION="remedial mathematics"
_top_of_script_pr
}
_less_important_function() { #...more logic...
one=2
: "${ACTION:="calligraphy"}"
_top_of_script_pr
}
现在已完成所有设置,因此您将在这里执行并提取结果。
_less_important_function
: "${PLACE:="the cemetery"}"
_more_important_function
: "${RESULT:="regret it."}"
_less_important_function
稍后,我将探讨为什么,但是运行上面的代码会产生以下结果:
_less_important_function()'s
第一次运行:我去了你母亲的家,在冰上看到了迪斯尼。
如果您做书法,您将成功。
然后
_more_important_function():
我去了公墓,在冰上看到迪斯尼。
如果您参加补习数学,您将获得成功。
_less_important_function()
再次:我去了公墓,在冰上看到迪斯尼。
如果您修数学,您会后悔的。
这里的主要功能是以下概念:conditional ${parameter} expansion.
您可以使用以下格式将变量设置为未设置或为null的值:
${var_name
:=desired_value}
相反,如果您只希望设置一个未设置的变量,则可以忽略:colon
和保留空值。
您可能会注意到,在上面的示例中$PLACE
,即使已经调用过$RESULT
via,它parameter expansion
也会被更改_top_of_script_pr()
,大概是在运行时对其进行设置。起作用的原因是它_top_of_script_pr()
是一个( subshelled )
函数-我将其包含在其中,parens
而不是{ curly braces }
用于其他函数。因为它是在子locally scoped
外壳中调用的,所以它设置的每个变量都是,当返回其父外壳时,这些值将消失。
但是当_more_important_function()
设置$ACTION
时globally scoped
它会影响_less_important_function()'s
第二次评估,$ACTION
因为_less_important_function()
设置$ACTION
仅通过${parameter:=expansion}.
为什么我用领导:colon?
嘛,man
页面会告诉你,: does nothing, gracefully.
你看,parameter expansion
正是这听起来像-它expands
的价值${parameter}.
因此,当我们设置一个变量,${parameter:=expansion}
我们留下了它的价值-这shell会尝试内联执行。如果它试图运行the cemetery
,只会向您吐出一些错误。PLACE="${PLACE:="the cemetery"}"
会产生相同的结果,但是在这种情况下也是多余的,我更喜欢shell: ${did:=nothing, gracefully}.
它确实允许您执行以下操作:
echo ${var:=something or other}
echo $var
something or other
something or other
顺便说一下-空值或未设置变量的内联定义也是以下原因起作用的原因:
<<HEREDOC echo $yo
${yo=yoyo}
HEREDOC
yoyo
想到a的最佳方法here-document
是将实际文件流式传输到输入文件描述符。它们或多或少就是它们的本质,但是不同的外壳对它们的实现略有不同。
在任何情况下,如果不引号,就<<LIMITER
可以将其流式传输并进行评估。expansion.
因此,在容器中声明变量是here-document
可行的,但只有expansion
这样,您才可以限制仅设置尚未设置的变量。尽管如此,这仍然完全符合您的描述需求,因为在调用模板打印功能时始终会设置默认值。
eval?
好吧,我提供的示例提供了一种安全有效的接受方法,parameters.
因为它可以处理范围,${parameter:=expansion}
所以可以从外部定义via中的每个变量。因此,如果将所有这些放到名为template_pr.sh的脚本中并运行:
% RESULT=something_else template_pr.sh
您会得到:
我去你母亲的家,在冰上看到迪士尼
如果你做书法,你会有所收获的
我去了公墓,看到冰上的迪士尼
如果您修数学,您将会有所成就
我去了公墓,看到冰上的迪士尼
如果您修数学,您将会有所成就
这对于在脚本中实际设置的变量不起作用,例如$EVENT, $ACTION,
和,$one,
但我仅以这种方式定义了这些变量以演示差异。
在任何情况下,接受未知的输入evaled
语句本质上都是不安全的,而parameter expansion
专门设计用来做到这一点。
您可以将占位符用于字符串模板,而不是未扩展的变量。这将很快变得混乱。如果您正在做的工作非常繁琐,那么您可能需要考虑使用具有真实模板库的语言。
format_template() {
changed_str=$1
for word in $changed_str; do
if [[ $word == %*% ]]; then
var="${word//\%/}"
changed_str="${changed_str//$word/${!var}}"
fi
done
}
str1='I went to %PLACE% and saw %EVENT%'
PLACE="foo"
EVENT="bar"
format_template "$str1"
echo "$changed_str"
上面的缺点是模板变量必须是它自己的单词(例如,您不能这样做"%prefix%foo"
)。可以通过一些修改来解决此问题,或者仅通过对模板变量进行硬编码而不是动态修改即可。