bashargumentsexpansion

Shell script: Bad Substitution for argument indices


Usually I find solutions for more complex tasks in shell scripts by breaking down longer commands into a sequence of simple commands. Here is one where I couldn't find a solution with this method.

I need to do calculations, in order to know which argument I need to use. Once I have it, I can call it, like here (simplified, generic version):

    1 i=1
    2 k=$(( i+1 ))
    3 echo $i
    4 echo $k
    5 echo "${!i}"
    6 echo "${!k}"

As to be expected:

    $ ./testo A B 
    1
    2
    A
    B

Firstly, I found that expression with an exclamation mark somewhere in the Interwebs, and I was happy to have it, because it works. I don't understand what it does, however. In my opinion, ! usually means not, but here it exactly works. Without, however, it doesn't. I'd like to learn, why.

Now I wanted to simplify the code, and remove the - I think - unnecessary second line by substituting the k in the last line with an arithmetic expression, as in line 2. To my surprise, neither of my efforts of copying the expression for k from the second line actually works:

    1 i=1
    2 k=$(( i+1 ))
    3 echo $i
    4 echo $k
    5 echo "${!i}"
    6 echo "${!k}"
    7 echo "${!$(( i+1 ))}"
    8 echo "${!(( i+1 ))}"
    9 echo "${!(( $i+1 ))}"

, as can be seen here:

    ./testo: line 7: ${!$(( i+1 ))}: bad substitution
    ./testo: line 8: ${!(( i+1 ))}: bad substitution
    ./testo: line 9: ${!(( $i+1 ))}: bad substitution

What do I do wrong; what would be a good substition; why can't I just copy the expression for k of line 2 into the place of k in line 6?


Solution

  • Expansions are not macros. To expand you have to have a variable. You can't optimize it. It's fine, presented code is perfect.

    I don't understand what it doe

    Indirect expansion is documented in bash manual. See What is indirect expansion? What does ${!var*} mean? and https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html .

    and remove the - I think - unnecessary second line by substituting the k in the last line with an arithmetic expression, as in line 2

    No possible.

    What do I do wrong

    Nothing.

    what would be a good substition

    k=$(( i+1 )); echo "${!k}" is perfect substitution

    why can't I just copy the expression for k of line 2 into the place of k in line 6?

    Expansions are not macros. The ${! has to expand a variable. You can't chain expansions, they are just not parsed or implemented that way.