Atribuição e expansão variáveis ao mesmo tempo

4

Por que

for i in {1..5}; do x="${i}" echo "$x"; done

não imprime

1
2
3
4
5

?

Qual é o caminho certo para fazer isso?

(% de for i in {1..5}; do x=$(i) echo "$x"; done testado

-bash: i: command not found

e outros)

    
por Sun 20.11.2017 / 01:45

3 respostas

7

Você pediu respostas para duas perguntas:

  1. Você solicitou uma explicação de por que seu código atual não produz o resultado esperado.

  2. Você pediu a maneira correta de escrever seu código para que ele produza a saída esperada.

Olhando para o seu código, posso ver duas explicações prováveis sobre por que você escreveu seu código da seguinte maneira:

  1. Pode haver alguma confusão sobre a sintaxe de um loop for .

  2. Pode haver alguma pequena confusão sobre a ordem de avaliação no que é chamado de simples comando .

sintaxe for-loop

No primeiro caso, eu diria que você está perdendo um ponto-e-vírgula após a atribuição da variável. Se você quiser escrever seu loop for em uma única linha, será necessário colocar um ponto e vírgula após cada comando no corpo do loop. Tente isso:

for i in {1..5}; do x="${i}"; echo "$x"; done

Outra alternativa seria escrever o for-loop usando a sintaxe de várias linhas com novas linhas no lugar do ponto-e-vírgula:

for i in {1..5}
do
x="${i}"
echo "${x}"
done

Você também pode misturar e ponto-e-vírgulas e novas linhas, por exemplo:

for i in {1..5}; do
x="${i}"; echo "${x}"
done

avaliação de comandos simples

No segundo caso, eu diria que você provavelmente assumiu que a atribuição de variável no prólogo do comando (ou seja, a atribuição x="$i" ) ocorre antes da expansão da variável no corpo do comando (ou seja, a expansão de ${x} em echo "${x}" ). Mas isso não é realmente o caso. Para verificar isso, podemos nos referir à página na expansão simples de comandos o Manual de Bash ou a subseção em comandos simples no Especificação de Posix . Ambas as referências incluem a seguinte passagem:

A "simple command" is a sequence of optional variable assignments and redirections, in any sequence, optionally followed by words and redirections, terminated by a control operator.

When a given simple command is required to be executed (that is, when any conditional construct such as an AND-OR list or a case statement has not bypassed the simple command), the following expansions, assignments, and redirections shall all be performed from the beginning of the command text to the end:

  1. The words that are recognized as variable assignments or redirections according to Shell Grammar Rules are saved for processing in steps 3 and 4.

  2. The words that are not variable assignments or redirections shall be expanded. If any fields remain following their expansion, the first field shall be considered the command name and remaining fields are the arguments for the command.

  3. Redirections shall be performed as described in Redirection.

  4. Each variable assignment shall be expanded for tilde expansion, parameter expansion, command substitution, arithmetic expansion, and quote removal prior to assigning the value.

Observe que a etapa 2 é onde ocorre a expansão da variável no comando, mas a etapa 1 informa que as atribuições de variáveis são salvas até as etapas 3 e 4. Segue-se que a expressão echo "${x}" é expandida para echo "" antes da atribuição x="${i}" ocorre. Isso explica por que você estava obtendo saída vazia.

Para uma discussão mais aprofundada sobre este tópico, consulte os seguintes posts:

por 20.11.2017 / 01:49
11

Respondendo sua pergunta conforme solicitado

Why does for i in {1..5}; do x="${i}" echo "$x"; done not output 1, 2, 3, 4, 5?

O motivo está relacionado à ordem de quais operações são avaliadas durante a execução. Olhe este comando

x="${i}" echo "$x"

O que isso faz é

  1. Substitua variáveis por seus valores
  2. Atribuir um valor temporário à variável x
  3. Execute o comando

Então você começa

  1. x=1 echo "" (ou x=2 echo "" , etc.)
  2. Para a duração desse comando, x é definido com o valor 1
  3. O comando é executado: echo ""

É provável que você queira que este comando seja de duas instruções: atribua um valor a x , exiba seu valor. Mas na sintaxe da shell o que você escreveu foi um código perfeitamente legal, então o shell executou-o sem objeção.

    
por 20.11.2017 / 01:55
2

Em zsh , usando uma terceira ${var::=value} dos operadores ${var=value} , ${var:=value} Bourne em que a atribuição é incondicional (em oposição a apenas se var não estiver definida / vazia).

for i in {1..5}; do echo ${x::=$i}; done

Ou:

for i ({1..5}) echo ${x::=$i}

Em bash :

set -o posix # so the value of x remains after eval returns
for i in {1..5}; do x=$i eval 'echo "$x"'; done

Ou seja, você precisa que $x tenha sido definido no momento em que o código que contém sua expansão for avaliado.

Para valores inteiros decimais como aqui, você também pode fazer:

for i in {1..5}; do echo "$((x = i))"; done

Ou você sempre pode usar o operador ${var:=value} Bourne depois de definir x como a string vazia.

for i in {1..5}; do x=; echo "${x:=$i}"; done
    
por 20.11.2017 / 14:29

Tags