递归替换变量

时间:2016-04-01 19:12:33

标签: regex bash recursion replace substitution

给定这个关联数组:

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

我想替换任何出现的模式@([^@/]+)@(例如@prefix@,其中prefix是捕获),其值与捕获相关联(例如所有值都/usr prefix},以便递归执行替换,直到不再出现。数组中每个键的步骤如下:

  1. 检索与其关联的值并对其执行(2)。
  2. 检查给定字符串中的模式是否匹配。
    • 如果没有,请返回给定的字符串。
    • 如果匹配:
      1. 对捕获执行(1)并保留结果。
      2. 用结果替换匹配。
      3. 对结果字符串执行(2)。
  3. 删除与该键关联的先前值,并将返回的最后一个字符串与其关联。
  4. 无论采用何种方法,所期望的结果都是:

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

    其他要求:

    • 不会出现自我引用(例如prefix=@prefix@)。
    • 如果可能,请仅使用Bash builtins。

    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
    

1 个答案:

答案 0 :(得分:2)

下面的(纯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