Utilizando a entrada do usuário para determinar quantas vezes um loop for itera

2

Estou com dificuldades para obter um loop for para a entrada do usuário na forma de um argumento para uma função que criei.

#!/bin/bash
number1=""    #Assume user input 5
loops=""      #Assume user input 5
if [[ ${number1} && ${loops} =~ ^-?[0.00-9.99]+$ ]];then
   for number2 in {1..${loops}}
   do
       Product=$(echo "${number1} * ${number2}" | bc -l)
       echo "${number1} * ${number2} = ${Product}"
       if test ${number2} -eq 7;then
           { echo "----------------------"; echo "${number2} is the best number."; echo "----------------------"; continue; }
       elif test ${number2} -eq 11;then
           { echo "------------------------------"; echo "${number2} is pretty awesome as well."; echo "------------------------------"; continue; }    
       fi 
   done
else
   { echo "Please try again with a valid numerical entry."; exit 1; }
fi

Este script não é executado quando o usuário insere um inteiro para a variável "loops" e qualquer número para "number1". number1 = 5 neste caso. Ele retorna o erro:

  

(standard_in) 1: erro de sintaxe

     

5 * {1..5} =

     

/home/$USER/For_Loop_Multiplication.sh: linha 9: teste: {1..5}: expressão inteira esperada

     

/home/$USER/For_Loop_Multiplication.sh: linha 11: teste: {1..5}: expressão inteira esperada

Se, em vez disso, eu inserir diretamente um limite final no número de loops diretamente no script, como o seguinte, e number1 = 5, ele será executado corretamente:

#!/bin/bash
number1=""     #Assume user input 5
if [[ ${number1} =~ ^-?[0.00-9.99]+$ ]];then
   for number2 in {1..5}
   do
       Product=$(echo "${number1} * ${number2}" | bc -l)
       echo "${number1} * ${number2} = ${Product}"
       if test ${number2} -eq 7;then
           { echo "----------------------"; echo "${number2} is the best number."; echo "----------------------"; continue; }
       elif test ${number2} -eq 11;then
           { echo "------------------------------"; echo "${number2} is pretty awesome as well."; echo "------------------------------"; continue; }    
       fi 
   done
else
   { echo "Please try again with a valid numerical entry."; exit 1; }
fi

Retorna o resultado:

  

5 * 1 = 5

     

5 * 2 = 10

     

5 * 3 = 15

     

5 * 4 = 20

     

5 * 5 = 25

Essencialmente, o que eu acho que está acontecendo é que o script parece estar colocando o intervalo inteiro como o valor number2, que é definido como o iterador no loop for. Como a entrada para a variável "loops" é um inteiro, não deve haver diferença entre perguntar ao usuário por um intervalo e colocá-lo no loop, ou simplesmente defini-lo como padrão quando eu escrevo o script. No entanto, aparentemente há uma diferença ... Qualquer percepção sobre a solução desse problema é apreciada. Obrigado.

    
por mike3759 24.12.2015 / 09:54

1 resposta

5

O problema é esta linha:

for number2 in {1..${loops}}

Esta é uma questão de precedência. Conforme explicado em man bash :

  

A ordem de expansões é: expansão de contraventamento; expansão do til,   expansão de parâmetros e variáveis, expansão aritmética e comando   substituição (feito da esquerda para a direita); divisão de palavras;   e expansão do caminho.

Portanto, a expansão de chaves acontece antes da expansão da variável antes . Portanto, quando as chaves são expandidas, $loops não foi avaliado como 5 . Isso significa que {1..$loops} (você não precisa do segundo {} ) expande para a string {1..5} e não a sequência de números de 1 a 5. O mesmo acontece se você usar uma string em vez de uma variável:

$ echo {1..5}  ## works: prints a sequence
1 2 3 4 5
$ echo {1..foo} ## fails: prints a string with no expansion
{1..foo}

Como solução alternativa, você pode usar seq :

for number2 in $(seq 1 "$loops"); do ...

Ou, melhor ainda, o loop certo para o trabalho:

for((number2=0;number2<=$loops; number2++)); do ...


Observação: Como regra geral, você também deve sempre citar suas variáveis.

    
por terdon 24.12.2015 / 10:40