Bash在变量中扩展变量

时间:2012-12-27 03:36:06

标签: bash

我正在尝试设置我的PS1提示变量以动态选择颜色。为此,我使用颜色名称定义了一堆局部变量:

$ echo $Green
\033[0;32m

但我希望在动态分配变量时使用它们,但我无法弄清楚如何正确扩展它们:

> colorstr="\${$color}"
> echo $colorstr
${Green}

我已经尝试了十几种evalecho和双引号的组合,但似乎都没有。扩展变量的逻辑方式(我认为)会导致错误:

> colorstr="${$color}"
-bash: ${$color}: bad substitution

(为清楚起见,我使用>代替$作为提示字符,但我使用的是bash)

如何扩展该变量?即以某种方式将“绿色”一词改为值\033[0;32m?并且优先使用bash或终端解析\033[0;32m也是绿色。

编辑:之前我误用了${!x}eval echo $x,所以我接受了这些作为解决方案。对于(也许是病态的)好奇,函数和PS1变量都在这个要点上:https://gist.github.com/4383597

5 个答案:

答案 0 :(得分:53)

使用eval是经典解决方案,但bash有更好的(更容易控制,更少犹豫)解决方案:

  • ${!colour}

Bash(4.1)reference manual说:

  

如果参数的第一个字符是感叹号(!),则是变量间接的级别   介绍。 Bash使用从参数的其余部分形成的变量的值   变量的名称;然后展开此变量,并在其余部分中使用该值   替换,而不是参数本身的值。这被称为间接   扩展

例如:

$ Green=$'\033[32;m'
$ echo "$Green" | odx
0x0000: 1B 5B 33 32 3B 6D 0A                              .[32;m.
0x0007:
$ colour=Green
$ echo $colour
Green
$ echo ${!colour} | odx
0x0000: 1B 5B 33 32 3B 6D 0A                              .[32;m.
0x0007:
$

odx命令非常非标准,但只是将数据转换为十六进制格式,右侧显示可打印字符。由于普通echo没有显示任何内容,我需要看到被回应的东西,我用了一个24年前我写的老朋友。)

答案 1 :(得分:11)

使用eval应该这样做:

green="\033[0;32m"
colorstr="green"
eval echo -e "\$$colorstr" test           # -e = enable backslash escapes
test

最后一次测试是绿色。

答案 2 :(得分:1)

Bash支持关联数组。当你可以使用dict时,不要使用间接。如果您没有关联数组,请升级到bash 4,ksh93或zsh。显然mksh最终也会添加它们,所以应该有很多选择。

function colorSet {
    typeset -a \
        clrs=(black red green orange blue magenta cyan grey darkgrey ltred ltgreen yellow ltblue ltmagenta ltcyan white) \
        msc=(sgr0 bold dim smul blink rev invis)

    typeset x

    while ! ${2:+false}; do
        case ${1#--} in
            setaf|setab)
                for x in "${!clrs[@]}"; do
                    eval "$2"'[${clrs[x]}]=$(tput "${1#--}" "$x")'
                done
                ;;
            misc)
                for x in "${msc[@]}"; do
                    eval "$2"'[$x]=$(tput "$x")'
                done
                ;;
            *)
                return 1
        esac
        shift 2        
    done
}

function main {
    typeset -A fgColors bgColors miscEscapes
    if colorSet --setaf fgColors --setab bgColors --misc miscEscapes; then
        if [[ -n ${1:+${fgColors[$1]:+_}} ]]; then
            printf '%s%s%s\n' "${fgColors[${1}]}" "this text is ${1}" "${miscEscapes[sgr0]}"
        else
            printf '%s, %s\n' "${1:-Empty}" 'no such color.' >&2
            return 1
        fi
    else
        echo 'Failed setting color arrays.' >&2
        return 1
    fi
}

main "$@"

虽然我们正在使用eval,但由于其他原因,它是一种不同类型的间接。请注意如何保证安全。

另请参阅:http://mywiki.wooledge.org/BashFAQ/006

答案 3 :(得分:0)

您需要为函数编写别名。查看http://tldp.org/LDP/abs/html/functions.html,体面的小教程和一些示例。

编辑:     对不起,看起来我误解了这个问题。首先,看起来您使用的变量是错误的,请查看http://www.thegeekstuff.com/2010/07/bash-string-manipulation/。还有什么调用这个脚本?您是将此添加到.bash_profile还是这是您的用户可以启动的脚本?使用导出应该使更改立即生效而无需重新编译。

var Green="\[\e[32m\]"
var Red="\[\e41m\]"

export PS1="${Green} welcome ${Red} user>"

答案 4 :(得分:0)

您的第一个结果显示了问题:

$ echo $Green
\033[0;32m

变量Green包含一串a backlash, a zero, a 3, etc.

Green="\033[0;32m"设定。因此,它不是颜色代码 需要解释变量内的文本(使用echo -e,printf或$' ...')。

让我用代码解释一下:

$ Green="\033[0;32m"    ;     echo "  $Green   test   "
  \033[0;32m   test     

你的意思是:

$  Green="$(echo -e "\033[0;32m" )"    ;     echo "  $Green   test   "
 test   

绿色环保。这可以打印颜色,但对PS1无用:

$  Green="\033[0;32m"    ;     echo -e "  $Green   test   "
 test   

因为这意味着字符串必须在echo -e解释之前才能解释。

更简单的方法(在bash中)是:

$ Green=$'\033[0;32m'    ;     echo "  $Green   test   "
  test   

请注意` $'...' `

解决了变量Green的问题后,通过var colorstr的值间接访问它是第二个问题,可以通过以下方法解决:

$ eval echo \$$colorstr testing colors
testing colors
$ echo ${!colorstr} testing colors
testing colors

注意请不要使用未引用的值(正如我在这里所做的那样,因为值在我的控制之下)。学会正确引用,例如:

$ eval echo \"\$$colorstr testing colors\"

有了这个,你可以写一个PS1等同于:

export PS1="${Green} welcome ${Red} user>"

使用:

Green=$'\033[0;32m'    Red=$'\033[0;31m'
color1=Green           color2=Red
export PS1="${!color1} welcome ${!color2} user>"