Chamando uma função que chama outra função com um loop for

4

Estou chamando uma função com um loop for e salvando o valor retornado em uma variável. Quando executo o código, recebo um erro command not found . O que está errado?

#!/bin/bash

check_1()
{
    x_$1=$(check_2 $1)
}
check_2()
{
    ans=$((3+$1))
    echo $ans
}
for((i=1; i<=2;i++))
do
  check_1 $i
  tmp=x_$i
  echo ${!tmp}
done

Se eu executar o script acima, obtenho:

sample.sh: line 5: x_1=4: command not found
sample.sh: line 5: x_2=5: command not found
    
por shiva 22.08.2014 / 13:23

3 respostas

2

Você deve, como diz Terdon em sua resposta , usar as matrizes associativas do bash.

Mas, se você insistir, e se você tiver um novo bash o suficiente, você pode usar um nameref (essa é a palavra mágica para procurar na manpage, a propósito).

i=1
declare -n tmp="foo_$i" # this is the nameref line
tmp="fooval"            # actually sets $foo_1
echo "$foo_1"           # prints fooval

Se dentro de uma função, use local -n em vez de declare -n para obter uma variável de função local.

    
por 22.08.2014 / 14:16
4

Você não pode definir nomes de variáveis dessa maneira. Você está tentando construir o nome da sua variável usando o valor de outra variável: x_$1=foo e isso não funcionará. Uma maneira muito melhor seria usar uma matriz:

#!/bin/bash

check_1()
{
    x[$1]=$(check_2 "$1")

}
check_2()
{
    ans=$((3+$1))
    echo "$ans"
}
for((i=1; i<=2;i++))
do
  check_1 "$i"
  echo "${x[$i]}"
done

Os retornos acima:

$ sample.sh
4
5
    
por 22.08.2014 / 13:37
1

Se você não estivesse trabalhando apenas com matemática, eu sugeriria o único outro método seguro / portátil que eu conheço que não requer testes muito rigorosos:

var1=string1
export "$var1=string2"
echo "$var1"
echo "$string1"

OUTPUT

string1
string2

Mas você está apenas trabalhando com matemática e, portanto, o problema é que você está echo ing o valor da variável em um subshell quando você deve defini-lo e avaliá-lo no shell atual.

i=0 ; until [ $((i=$i+1)) -ge 3 ]
do  echo "\$x_$i = $((x_$i=3+$i))"
done     
echo $x_1 $x_2

OUTPUT

$x_1 = 4
$x_2 = 5
4 5

Então o que você está fazendo é apenas matemática. O interessante sobre a aritmética de shell é que você pode usá-lo para avaliar duas vezes com segurança uma variável no shell atual - não há subshells.

POSIX tem o que dizer sobre isso :

A expansão aritmética fornece um mecanismo para avaliar uma expressão aritmética e substituir seu valor. O formato da expansão aritmética será o seguinte:

$((expression))

The expression shall be treated as if it were in double-quotes, except that a double-quote inside the expression is not treated specially. The shell shall expand all tokens in the expression for parameter expansion, command substitution, and quote removal.

Talvez mais interessante, também tem a dizer:

All changes to variables in an arithmetic expression shall be in effect after the arithmetic expansion, as in the parameter expansion "${x=value}".

If the shell variable x contains a value that forms a valid integer constant, optionally including a leading plus or minus sign, then the arithmetic expansions "$((x))" and "$(($x))" shall return the same value.

Portanto, se uma variável é definida dentro do contexto de uma substituição aritmética, essa definição persiste no ambiente atual. Como:

echo $((x=1)); echo $x
1
1

Mas se você adicionar a isso as outras partes - especificamente que o shell deve expandir todos os tokens e que "$((x))" e "$(($x))" devolverão o mesmo valor . Você pode ver como o loop until acima funciona.

Então:

$((x_ #just a string
     $i #integer value
        = #assignment operator
          3 + #addition
              $i )) #same integer value

Você vê que o shell precisa expandir o $i em ambos os contextos - o do token shell variável como uma cadeia antes do operador de atribuição e o do valor inteiro no qual ele executa a operação de adição, cuja soma é atribuído a x_$i .

Os arrays podem ser úteis, mas, sem contar um formulário, eles não são portáteis e todos vêm com o próprio snaffus específico da implementação. E além disso - esta é a resposta para a pergunta que você fez.

É por isso que a seguinte função funciona:

defv() {
    [ -n "${1##*[!0-9]*}" ] && # verify $1 is numeric only
    v=x_$1 &&                  # redefine caller loop var
    : $((x_$1=5+$1))           # : do nothing but expand arg
}

for v in 1 2 3 4 5             # init array
do  defv $v                    # defv() $indirection
    echo "$v = $(($v))"        # \$x_$indirection = $x_$indirection
done

OUTPUT

x_1 = 6
x_2 = 7
x_3 = 8
x_4 = 9
x_5 = 10

Suas funções:

check_1()
{
    check_2 x_$1 $1
}
check_2()
{
    : $(($1=3+$2))
}
for((i=1; i<=2;i++))
do
  check_1 $i
  tmp=x_$i
  echo ${!tmp}
done

OUTPUT

4
5
    
por 22.08.2014 / 15:11