这是一种解决方案,可以正确解析引号并在未给出引号时以空格终止。这是安全的:没有使用eval。
我在我的 .bashrc 和 .zshrc 中使用此代码从 shell 脚本中导入变量:
# Usage: _getvar VARIABLE_NAME [sourcefile...]
# Echos the value that would be assigned to VARIABLE_NAME
_getvar() {
local VAR="$1"
shift
awk -v Q="'" -v QQ='"' -v VAR="$VAR" '
function loc(text) { return index($0, text) }
function unquote(d) { $0 = substr($0, eq+2) d; print substr($0, 1, loc(d)-1) }
{ sub(/^[ \t]+/, ""); eq = loc("=") }
substr($0, 1, eq-1) != VAR { next } # assignment is not for VAR: skip
loc("=" QQ) == eq { unquote(QQ); exit }
loc("=" Q) == eq { unquote( Q); exit }
{ print substr($1, eq + 1); exit }
' "$@"
}
这会保存所需的变量名,然后移动参数数组,以便将其余的作为文件传递给awk。
因为在awk 中调用shell 变量和引用引号字符非常困难,所以我在命令行中将它们定义为awk 变量。 Q 是单引号(撇号)字符,QQ 是双引号,VAR 是我们之前保存的第一个参数。
为了更方便,有两个辅助函数。第一个返回当前行中给定文本的位置,第二个使用引号字符d(用于“分隔符”)打印行中前两个引号之间的内容。有一个杂散的d 连接到第一个substr 以防止多行字符串(请参阅下面的“注意事项”)。
虽然我编写了用于 POSIX shell 语法解析的代码,但它似乎与您的格式仅不同之处在于分配周围是否有空格。您可以通过在 awk 的第 4 行的 sub(…) 之前添加 sub(/[ \t]*=[ \t]*/, "="); 来将该功能添加到上述代码中(注意:第 1 行是空白的)。
第四行去掉前导空白并保存第一个等号的位置。请确认您的awk 支持\t 作为选项卡,这在古代 UNIX 系统上无法保证。
substr 行将等号之前的文本与VAR 进行比较。如果不匹配,则该行正在分配一个不同的变量,因此我们跳过它并移至下一行。
现在我们知道我们已经得到了请求的变量赋值,所以只需解开引号即可。我们通过搜索="(第6行)或='(第7行)或不带引号(第8行)的第一个位置来做到这一点。这些行中的每一行都打印分配的值。
警告:如果有转义的引号字符,我们将返回一个截断的值。检测到这一点有点不重要,我决定不实施它。还有一个多行引号的问题,它在第一个换行符处被截断(这是上面提到的“流浪d”的目的)。此页面上的大多数解决方案都存在这些问题。