【问题标题】:Bash array parameter expansion within parameter name: bad substitution参数名称内的 Bash 数组参数扩展:错误替换
【发布时间】:2011-08-22 03:48:23
【问题描述】:

我编写了一个脚本,希望用户在其中输入一个或多个目录,并检查每个目录中是否存在对本次讨论不重要的内容。所有内部目录也会被检查到指定的深度。

虽然我可以声明数组Directories0(输入目录)来启动,但我无法以任何方式引用它...结果:替换错误。显然,Directories1 的深度为 1,Directories2 的深度为 2,以此类推……

下面是一段sn-p代码:

let Recurse=4               ## say... variable value ##
[ "$Recurse" ] && let MaxDepth="$Recurse" || let MaxDepth=0
declare -i depth=0

IFS=$'\n'
## declare -a Directories${depth}=("${@}")      ##  <—— doesn't work
## declare -a Directories${depth}="("${@}")"        ##  <—— works if the brackets only are quoted...
## declare -a Directories${depth}=\("${@}"\)        ##  <—— ... or escaped
declare -a "Directories${depth}=("${@}")"
IFS=$' \t\n'

## Nested loop, depth counter increases for each directory depth. I want to stop at a specific depth which is entered as an option ##
for (( depth = 0; depth <= MaxDepth; depth++ )); do                  ## MaxDepth is entered as option ##
    until [ -z "${Directories${depth}[*]}" ]; do                       ## ***** bad substitution error ***** ##
        declare input="$(follow "${Directories${depth}[0]}")"       ## follow is a script that resolves symlinks and Finder aliases ##
        CheckDirectory "${input%/}/"                                  ## check directory ##
        case $? in

        ## Tests passed ##
        0)  if [[ "$Recurse" && "$depth" -lt "$MaxDepth" ]]; then
                IFS=$'\n'
                ## get ready to check sub-directories ##
                declare -a Directories$(( depth + 1 ))="("${Directories$(( depth + 1 ))[@]}" $(find -P "${Directories${depth}[0]}" -type d -mindepth 1 -maxdepth 1 -exec follow '{}' \;))"
                IFS=$' \t\n'
            fi
            true;;

        ## Tests failed ##
        *)  false;;
        esac
        [ $? -eq 0 ] && unset Directories${depth}[0] || exit 1             ## if test fails, exit, if succeeds, move on to next directory ##
        declare -a Directories${depth}="("${Directories${depth}[@]}")"      ## re-shuffle the array to get rid of null value at index 0 ##
        (( element++ ))
    done
done

下面是一个简化版,如果你不想通过上面的代码,这是问题的症结所在:

depth=2
declare -a "Directories${depth}=(yo man ma me mo)"
echo "${Directories${depth}[4]}"
    > -bash: ${Directories${depth}[4]}: bad substitution
echo "${Directories2[4]}"
    > mo

解决方案,有人吗?

【问题讨论】:

    标签: bash shell variables recursion parameters


    【解决方案1】:

    您需要在 ${} 构造中使用文字变量名。如果要引用名称在运行时确定的变量,则需要显式地经过一个间接级别。

    name="Directories${depth}[4]"
    echo ${!name}
    

    这对分配没有帮助。只要您在当前范围内进行分配(而不是在包含范围内),您就可以使用内置的 typeset 进行计算分配。但是,请注意:bash 具有启发式方法,可以将看起来像数组赋值语法的赋值转换为数组赋值。这意味着如果元素要存储绝对文件名(始终以 / 开头),则下面的代码是可以的,但如果要存储任意文件名(可能类似于 (foo))则不行.

    typeset "Directories${depth}[4]=new value"
    

    另外,可以使用eval 对在运行时使用任何shell 确定名称的变量执行赋值。这具有在任何 shell 中工作的优势,而不仅仅是 bash。但是,您需要非常小心:很难正确引用。最好尽量少做eval 的论点。使用临时变量获取存储值。

    eval "tmp=\${Directories${depth}[4]}"
    echo "$tmp"
    tmp="new value"
    eval "Directories${depth}[4]=\$tmp"
    

    【讨论】:

    • 好的,在炸毁我的代码后(现在 > 3 倍大......而且启动速度慢得多),我通过很多很多循环将我的所有数组变成了常规参数分配。很多不必要的代码。 eval 和间接引用都很棒,但是对于数组来说,无论是分配还是引用,都非常糟糕。是否有NO WAY一次性处理数组赋值?喜欢:typeset -a Directories${depth}=("${Array[@]}")?或者,我需要什么:typeset -a Directories${depth}=("${Directories${depth}[@]}") 使用 eval?间接引用?还有什么技巧?也许是另一种语言?珀尔? C? php?
    • @Aesthir 的确,我认为 Perl 正是您要找的。​​span>
    • 感谢 Giles 的深思熟虑的答案,cleary 花了一分钟多的时间才输入。我已经研究了很长一段时间,并尝试了许多(100 次)快速测试,主要是单行,修改语法或从不同角度解决问题......但我仍然被卡住了。如果 Perl 可以提供解决方案,我不知道。 Beyone Perl 正则表达式,我不知道关于 Perl 的第一件事(用法/语法/命令/等)。 您能否提供一些 Perl 代码,例如,在我的脚本底部的第 4 行重新索引数组?我敢肯定它会是一个单行...
    • @Aesthir Perl 一开始是 shell 和 awk 合二为一,从那时起已经发展了很多。它对嵌套数据结构有很好的支持,这就是您在此处所追求的(按深度和广度索引的目录)。我不知道“重新索引数组”是什么意思,但是如果更改现有元素,则相当于此处的赋值是 $Directories[$depth][4] = "new value",或者将 push @{$Directories[$depth]}, "new value" 附加到现有数组的末尾。
    • 通过“重新索引数组”我的意思是如何在上面的原始脚本中获取这一行(从底部开始第四个)以使用 bash Perl,不问题:declare -a Directories${depth}="("${Directories${depth}[@]}")" 基本上,这是我希望运行的命令:typeset -a Directories0=("${Directories0[@]}") ...我如何让它工作(任何语言)?
    【解决方案2】:

    使用 eval,这行得通:

    eval echo \${Directories${depth}[4]}
    
    mo
    

    【讨论】:

    • 是的,好吧,这个简单的例子有效,没有空格。但是这个简单的例子在我的代码中找不到,它只是用来说明我的问题。问题的关键在于代码,如果您不想让整个事情正常工作,那么关注从底部算起的第 4 行,并在不使用 eval 循环遍历每个元素的情况下使其正常工作乙>。请记住,这些值不仅仅是“mo”,而是带有空格和 Unicode 字符的长行,远远超出了基本的拉丁语。我提出了eval确实工作的概念,但也许我用错了。
    【解决方案3】:

    尝试评估

    #!/bin/bash 深度=2 声明 -a "Directories${depth}=(yo man ma me mo)" eval echo "\${Directories${depth}[4]}" 回声“${Directories2[4]}”

    【讨论】:

    • eval 的问题是我似乎无法用 arrays 做任何事情。 eval 分解包含空格的元素,无论 IFS 设置为什么。我试了又试,但无济于事...这是一个挑战,尝试使用eval获取我作为问题发布的代码...我唯一能想到的就是循环遍历每个元素我希望使用 eval 的时间。对于初学者,无需循环,使用eval 让底部的第 4 行开始工作(重新洗牌)。如果eval真的可以做到这一点,我相信这将是一个简单的单行。
    【解决方案4】:

    你快到了:

    declare -a Directories${depth}="( yo man ma me mo )"
    

    有效(并且没有 eval,顺便说一句)。要访问该值,请使用 ${!} 语法:

    temp=Directories$depth[@]
    echo ${!temp}
    

    【讨论】:

      【解决方案5】:

      也许这是对所问问题的(迟来的)答案? (这个问题并不像提问者认为的那么清楚,我觉得......)

      #!/bin/bash
      
      for depth in {0..5}
      do
        var_value=(yo man ma me mo "last value")
        var_name="directories${depth}"
        eval "${var_name}=\"${var_value[${depth}]}\""
      
        value="directories${depth}"
        printf "directories{$depth} = ${!value}"
      
        [ $depth -eq 0 ] && printf "  \t directories0=$directories0\n"
        [ $depth -eq 1 ] && printf "  \t directories1=$directories1\n"
        [ $depth -eq 2 ] && printf "  \t directories2=$directories2\n"
        [ $depth -eq 3 ] && printf "  \t directories3=$directories3\n"
        [ $depth -eq 4 ] && printf "  \t directories4=$directories4\n"
        [ $depth -eq 5 ] && printf "  \t directories5=$directories5\n"
      done
      

      产生:

      directories{0} = yo      directories0=yo
      directories{1} = man     directories1=man
      directories{2} = ma      directories2=ma
      directories{3} = me      directories3=me
      directories{4} = mo      directories4=mo
      directories{5} = last value      directories5=last value
      

      主要的一点是,如果一个变量名由另一个变量组成,如何为它赋值。例如,如果设置“foobar=value”是将变量设置为值的常规方法,那么如果 x=foo 和 y=bar,如何设置“${x}${y}=value”=>例如:

        foobar=test
        echo $foobar
        > test
      
        x=foo
        y=bar
        eval "export ${x}${y}=\"value\""
        echo $foobar
        > value
      

      如果我误解了这个问题,好吧,我并不感到非常惊讶 :-)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多