bash:函数 + 源代码 + 声明 = 繁荣
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2891473/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
bash: function + source + declare = boom
提问by Chen Levy
Here is a problem:
这里有一个问题:
In my bash scripts I want to source several file with some checks, so I have:
在我的 bash 脚本中,我想通过一些检查来获取几个文件,所以我有:
if [ -r foo ] ; then
source foo
else
logger -t function safe_source() {
if [ -r ] ; then
source
else
logger -t declare GLOBAL_VAR=42
-p crit "unable to source "
exit 1
fi
}
safe_source foo
safe_source bar
# ... etc ...
-p crit "unable to source foo"
exit 1
fi
if [ -r bar ] ; then
source bar
else
logger -t function safe_source() {
# ...
declare GLOBAL_VAR=42
# ...
}
-p crit "unable to source bar"
exit 1
fi
# ... etc ...
Naively I tried to create a function that do:
我天真地尝试创建一个函数:
$> a_var_name="color"
$> eval ${a_var_name}="blue"
$> echo -e "The color is ${color}."
The color is blue.
But there is a snag there.
但有一个障碍。
If one of the files foo, bar, etc. have a global such as --
如果其中一个文件foo,bar等具有全局变量,例如 -
function safe_source() { echo eval " \
if [ -r ] ; then \
source ; \
else \
logger -t `safe_source foo`
`safe_source bar`
-p crit \"unable to source \" ; \
exit 1 ; \
fi \
"; }
-- it will effectively become:
——它将有效地变成:
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 '
thus a global variable becomes local.
因此全局变量变为局部变量。
The question:
问题:
An alias in bash seems too weak for this, so must I unroll the above function, and repeat myself, or is there a more elegant approach?
bash 中的别名对此似乎太弱了,所以我必须展开上述功能并重复自己,还是有更优雅的方法?
... and yes, I agree that Python, Perl, Ruby would make my life easier, but when working with legacy system, one doesn't always have the privilege of choosing the best tool.
...是的,我同意 Python、Perl、Ruby 会让我的生活更轻松,但是在使用遗留系统时,人们并不总是有选择最佳工具的特权。
回答by Ryan B. Lynch
Yes, Bash's 'eval' command can make this work. 'eval' isn't very elegant, and it sometimes can be difficult to understand and debug code that uses it. I usually try to avoid it, but Bash often leaves you with no other choice (like the situation that prompted your question). You'll have to weigh the pros and cons of using 'eval' for yourself.
是的,Bash 的 'eval' 命令可以完成这项工作。'eval' 不是很优雅,有时很难理解和调试使用它的代码。我通常会尽量避免它,但 Bash 经常让您别无选择(例如提示您提问的情况)。您必须权衡自己使用 'eval' 的利弊。
Some background on 'eval'
'eval' 的一些背景
If you're not familiar with 'eval', it's a Bash built-in command that expects you to pass it a string as its parameter. 'eval' dynamically interprets and executes your string as a command in its own right, in the current shell context and scope. Here's a basic example of a common use (dynamic variable assignment):
如果您不熟悉 'eval',那么它是一个 Bash 内置命令,它希望您将一个字符串作为参数传递给它。'eval' 在当前的 shell 上下文和范围内动态地解释和执行你的字符串作为一个命令。这是一个常见用途的基本示例(动态变量分配):
eval if '[' -r <INCL_FILE> ']' ';' then source <INCL_FILE> ';' else logger -t <SCRIPT_NAME> -p crit '"unable' to source '<INCL_FILE>"' ';' exit 1 ';' fi
See the Advanced Bash Scripting Guide for more info and examples: http://tldp.org/LDP/abs/html/internal.html#EVALREF
有关更多信息和示例,请参阅高级 Bash 脚本指南:http: //tldp.org/LDP/abs/html/internal.html#EVALREF
Solving your 'source' problem
解决您的“来源”问题
To make 'eval' handle your sourcing issue, you'd start by rewriting your function, 'safe_source()'. Instead of actually executing the command, 'safe_source()' should just PRINT the command as a string on STDOUT:
为了让“eval”处理您的采购问题,您首先要重写您的函数“safe_source()”。'safe_source()' 应该只是将命令打印为 STDOUT 上的字符串,而不是实际执行命令:
'[' -r <INCL_FILE> ']'
source <INCL_FILE>
Also, you'll need to change your function invocations, slightly, to actually execute the 'eval' command:
此外,您需要稍微更改函数调用,以实际执行 'eval' 命令:
declare -g DATA="Hello World, meow!"
(Those are backticks/backquotes, BTW.)
(那些是反引号/反引号,顺便说一句。)
How it works
这个怎么运作
In short:
简而言之:
- We converted the function into a command-string emitter.
- Our new function emits an 'eval' command invocation string.
- Our new backticks call the new function in a subshell context, returning the 'eval' command string output by the function back up to the main script.
- The main script executes the 'eval' command string, captured by the backticks, in the main script context.
- The 'eval' command string re-parses and executes the 'eval' command string in the main script context, running the whole if-then-else block, including (if the file exists) executing the 'source' command.
- 我们将该函数转换为命令字符串发射器。
- 我们的新函数发出一个“eval”命令调用字符串。
- 我们的新反引号在子 shell 上下文中调用新函数,将函数输出的“eval”命令字符串返回到主脚本。
- 主脚本在主脚本上下文中执行由反引号捕获的“eval”命令字符串。
- 'eval' 命令字符串在主脚本上下文中重新解析并执行 'eval' 命令字符串,运行整个 if-then-else 块,包括(如果文件存在)执行 'source' 命令。
It's kind of complex. Like I said, 'eval' is not exactly elegant. In particular, there are a couple of special things you should notice about the changes we made:
这有点复杂。就像我说的,'eval' 并不完全优雅。特别是,关于我们所做的更改,您应该注意一些特别的事情:
- The entire IF-THEN-ELSE block has becomes one whole double-quoted string, with backslashes at the end of each line "hiding" the newlines.
- Some of the shell special characters like '"') have been backslash-escaped, while others ('$') have been left un-escaped.
- 'echo eval' has been prepended to the whole command string.
- Extra semicolons have been appended to all of the lines where a command gets executed to terminate them, a role that the (now-hidden) newlines originally performed.
- The function invocation has been wrapped in backticks.
- 整个 IF-THEN-ELSE 块变成了一个完整的双引号字符串,每一行末尾的反斜杠“隐藏”了换行符。
- 一些 shell 特殊字符,如 '"') 已被反斜杠转义,而其他 ('$') 则未转义。
- 'echo eval' 已添加到整个命令字符串中。
- 额外的分号已附加到执行命令以终止它们的所有行,这是(现在隐藏的)换行符最初执行的角色。
- 函数调用已被包装在反引号中。
Most of these changes are motived by the fact that 'eval' won't handle newlines. It can only deal with multiple commands if we combine them into a single line delimited by semicolons, instead. The new function's line breaks are purely a formatting convenience for the human eye.
大多数这些更改的动机是 'eval' 不会处理换行符。如果我们将它们组合成由分号分隔的一行,它只能处理多个命令。新函数的换行符纯粹是为了人眼的格式化方便。
If any of this is unclear, run your script with Bash's '-x' (debug execution) flag turned on, and that should give you a better picture of exactly what's happening. For instance, in the function context, the function actually produces the 'eval' command string by executing this command:
如果其中任何一个不清楚,请在 Bash 的“-x”(调试执行)标志打开的情况下运行您的脚本,这应该可以让您更好地了解正在发生的事情。例如,在函数上下文中,该函数通过执行以下命令实际上生成了 'eval' 命令字符串:
##代码##Then, in the main context, the main script executes this:
然后,在主上下文中,主脚本执行以下操作:
##代码##Finally, again in the main context, the eval command executes these two commands if exists:
最后,再次在主上下文中,如果存在,则 eval 命令执行这两个命令:
##代码##Good luck.
祝你好运。
回答by Reishin
It's a bit late answer, but now declaresupports a -gparameter, which makes a variable global (when used inside function). Same works in sourced file.
答案有点晚,但现在declare支持一个-g参数,该参数使变量成为全局变量(在函数内部使用时)。在源文件中同样有效。
If you need a global (read exported) variable, use:
如果您需要全局(读取导出)变量,请使用:
##代码##回答by Paused until further notice.
declareinside a function makes the variable local to that function. exportaffects the environment of child processes not the current or parent environments.
declare在函数内部使变量成为该函数的局部变量。export影响子进程的环境而不是当前或父环境。
You can set the values of your variables inside the functions and do the declare -r, declare -ior declare -riafter the fact.
您可以在函数内设置变量的值,然后执行declare -r,declare -i或declare -ri事后执行。

