在我的bash脚本中,我想通过一些检查来源几个文件,所以我有:
if [ -r foo ] ; then source foo else logger -t $0 -p crit "unable to source foo" exit 1 fi if [ -r bar ] ; then source bar else logger -t $0 -p crit "unable to source bar" exit 1 fi # ... etc ...
天真的我试图创建一个功能:
function safe_source() { if [ -r $1 ] ; then source $1 else logger -t $0 -p crit "unable to source $1" exit 1 fi } safe_source foo safe_source bar # ... etc ...
但那里有一个障碍.
如果其中一个文件foo,bar等具有全局如 –
declare GLOBAL_VAR=42
– 它将有效地成为:
function safe_source() { # ... declare GLOBAL_VAR=42 # ... }
因此全局变量变为局部变量.
问题是:
bash中的别名似乎太弱了,所以我必须展开上面的函数,并重复自己,还是有更优雅的方法?
……是的,我同意Python,Perl,Ruby会让我的生活更轻松,但在使用遗留系统时,人们并不总是有选择最佳工具的特权.
‘eval’的一些背景
如果您不熟悉’eval’,那么它是一个Bash内置命令,希望您将字符串作为参数传递给它. ‘eval’在当前的shell上下文和范围中动态地解释并执行您的字符串作为命令.以下是常用的基本示例(动态变量赋值):
$> a_var_name="color" $> eval ${a_var_name}="blue" $> echo -e "The color is ${color}." The color is blue.
有关详细信息和示例,请参阅“高级Bash脚本编制指南”:http://tldp.org/LDP/abs/html/internal.html#EVALREF
解决您的“来源”问题
要使’eval’处理您的采购问题,您首先要重写您的函数’safe_source()’.而不是实际执行命令,’safe_source()’应该只是将命令作为STDOUT上的字符串PRINT:
function safe_source() { echo eval " \ if [ -r $1 ] ; then \ source $1 ; \ else \ logger -t $0 -p crit \"unable to source $1\" ; \ exit 1 ; \ fi \ "; }
`safe_source foo` `safe_source bar`
(那些是反击/反引号,BTW.)
这个怎么运作
简而言之:
>我们将函数转换为命令串发射器.
>我们的新函数发出’eval’命令调用字符串.
>我们的新反引号在子shell上下文中调用新函数,将函数返回的’eval’命令字符串返回到主脚本.
>主脚本在主脚本上下文中执行由反引号捕获的’eval’命令字符串.
>’eval’命令字符串在主脚本上下文中重新解析并执行’eval’命令字符串,运行整个if-then-else块,包括(如果文件存在)执行’source’命令.
这有点复杂.就像我说的那样,’eval’并不完美.特别是,您应该注意到有关我们所做的更改的一些特殊事项:
>整个IF-THEN-ELSE块已成为一个完整的双引号字符串,每行末尾的反斜杠“隐藏”换行符.
>某些shell特殊字符(如’“’)已被反斜杠转义,而其他(‘$’)则未被转义.
>’echo eval’已添加到整个命令字符串中.
>额外的分号已附加到执行命令以终止它们的所有行,这是(现在隐藏的)换行符最初执行的角色.
>函数调用已包含在反引号中.
大多数这些变化是由’eval’无法处理换行的事实所驱动的.如果我们将它们组合成由分号分隔的单行,它只能处理多个命令.新功能的换行符纯粹是人眼的格式化便利.
如果其中任何一个不清楚,请在Bash的’-x'(调试执行)标志打开的情况下运行您的脚本,这样可以让您更准确地了解正在发生的事情.例如,在函数上下文中,函数通过执行以下命令实际生成’eval’命令字符串:
echo eval ' if [ -r <INCL_FILE> ] ; then source <INCL_FILE> ; else logger -t <SCRIPT_NAME> -p crit "unable to source <INCL_FILE>" ; exit 1 ; fi '
然后,在主上下文中,主脚本执行:
eval if '[' -r <INCL_FILE> ']' ';' then source <INCL_FILE> ';' else logger -t <SCRIPT_NAME> -p crit '"unable' to source '<INCL_FILE>"' ';' exit 1 ';' fi
最后,再次在主上下文中,eval命令执行这两个命令(如果存在):
'[' -r <INCL_FILE> ']' source <INCL_FILE>
祝好运.