递归替换变量

Recursively replacement on a variable

给定这个关联数组:

declare -A variables=(
   [prefix]='/usr'
   [exec_prefix]='@prefix@'
   [libdir]='@exec_prefix@/lib'
)

我想用与捕获关联的值(例如 /usr for prefix) 在其所有值中,以便递归执行替换,直到不再出现为止。数组中每个键的步骤为:

  1. 检索与其关联的值并对其执行 (2)。
  2. 检查给定字符串中是否有匹配的模式。
    • 如果没有,return给定的字符串。
    • 如果有匹配项:
      1. 对捕获执行 (1) 并保留结果。
      2. 用结果替换匹配项。
      3. 对结果字符串执行 (2)。
  3. 删除与键关联的先前值并将最后一个字符串关联到它 returned.

无论采用哪种方法,期望的结果都是:

prefix=/usr
exec_prefix=/usr
libdir=/usr/lib

附加要求:


Lua中的示例:

local variables={
    prefix="/usr",
    exec_prefix="@prefix@",
    includedir="@prefix@/include",
    libdir="@exec_prefix@/lib",
    random_one_to_show_off_fancy_recursion="@prefix@@libdir@@includedir@"
}

function replacer( variable )
    return compute_value(variables[variable])
end

function compute_value( s )
    return s:gsub('@([^@/]+)@',replacer)
end

local variable, value = next(variables)
while variable do
    variables[variable] = compute_value(value)

    print( string.format('%-39s\t%s', variable, variables[variable]) )

    variable, value = next(variables,variable)
end

下面的(纯 Bash)代码假设当 'xyz' 不是变量时,“@@”保持不变,“@xyz@”保持不变。它还尝试检测递归变量定义,包括间接变量定义(例如 [a]=@b@ [b]=@c@ [c]=@a@)。

# Regular expression for a string with an embedded expansion
# For a string of the form 'u@v@w', where 'u' and 'v' do not contain '@':
#   u -> BASH_REMATCH[1]
#   v -> BASH_REMATCH[2]
#   w -> BASH_REMATCH[3]
readonly EXPANSION_RX='^([^@]*)@([^@]*)@(.*)$'

# First pass tries to expand all variables
vars_to_expand=( "${!variables[@]}" )

while (( ${#vars_to_expand[*]} > 0 )) ; do
    old_vars_to_expand=( "${vars_to_expand[@]}" )
    vars_to_expand=()
    for var in "${old_vars_to_expand[@]}" ; do
        val=${variables[$var]}
        unexpanded=$val
        newval=

        while [[ $unexpanded =~ $EXPANSION_RX ]] ; do
            newval+=${BASH_REMATCH[1]}
            v=${BASH_REMATCH[2]}
            unexpanded=${BASH_REMATCH[3]}

            if [[ $v == "$var" ]] ; then
                echo "ERROR - Expanding '@$var@' in '$var'" >&2
                exit 1
            elif [[ -z $v ]] ; then
                # The empty string can not be a hash key (Duh!)
                newval+=@$v@
            else
                newval+=${variables[$v]-@$v@}
            fi
        done

        newval+=$unexpanded

        if [[ $newval != "$val" ]] ; then
            # An expansion has occurred.

            # Update the variable value
            variables[$var]=$newval

            # Further expansions may be possible, so add the variable to the
            # list of variables to be expanded again
            vars_to_expand+=( "$var" )
        fi
    done
done