
Porque
for i in {1..5}; do x="${i}" echo "$x"; done
não saída
1
2
3
4
5
?
Qual é a maneira certa de fazer isso?
(Testadofor i in {1..5}; do x=$(i) echo "$x"; done
-bash: i: comando não encontrado
e outros)
Responder1
Respondendo à sua pergunta conforme solicitado
Por que
for i in {1..5}; do x="${i}" echo "$x"; done
não gera1
,2
,3
,4
,5
?
O motivo tem a ver com a ordem em que as operações são avaliadas durante a execução. Veja este comando
x="${i}" echo "$x"
O que isso faz é
- Substitua as variáveis pelos seus valores
- Atribuir um valor temporário à variável
x
- Execute o comando
Então você consegue
x=1 echo ""
(oux=2 echo ""
, etc.)- Durante a duração deste comando,
x
é definido como o valor1
- O comando é executado:
echo ""
É provável que você quisesse que este comando fosse duas instruções: atribua um valor a x
e produza seu valor. Mas na sintaxe do shell o que você escreveu era um código perfeitamente legal, então o shell o executou sem objeções.
Responder2
Você pediu respostas para duas perguntas:
Você pediu uma explicação de por que seu código atual não produz a saída esperada.
Você solicitou 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 para o motivo pelo qual você escreveu seu código dessa maneira:
Pode haver alguma ligeira confusão sobre osintaxe de um loop for.
Pode haver alguma ligeira confusão sobre a ordem de avaliação no que é chamado decomando simples.
sintaxe do loop for
No primeiro caso, eu diria que está faltando um ponto e vírgula após a atribuição da variável. Se você deseja escrever seu loop for em uma única linha, você precisa colocar um ponto e vírgula após cada comando no corpo do loop. Em vez disso, tente isto:
for i in {1..5}; do x="${i}"; echo "$x"; done
Outra alternativa seria escrever o loop for usando a sintaxe multilinha 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 combinar ponto-e-vírgula 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 x="$i"
atribuição) ocorre antes da expansão da variável no corpo do comando (ou seja, a expansão de ${x}
in echo "${x}"
). Mas este não é realmente o caso. Para verificar isso podemos consultar a página emexpansão de comando simplesno Manual do Bash ou nosubseção sobre comandos simplesnoEspecificação Posix. Ambas as referências incluem a seguinte passagem:
Um "comando simples" é uma sequência de atribuições e redirecionamentos de variáveis opcionais, em qualquer sequência, opcionalmente seguidos por palavras e redirecionamentos, finalizados por um operador de controle.
Quando um determinado comando simples precisa ser executado (isto é, quando qualquer construção condicional, como uma lista AND-OR ou umcasoinstrução não ignorou o comando simples), as seguintes expansões, atribuições e redirecionamentos devem ser executados do início ao final do texto do comando:
As palavras que são reconhecidas como atribuições variáveis ou redirecionamentos de acordo comRegras gramaticais do shellsão salvos para processamento nas etapas 3 e 4.
As palavras que não sejam atribuições de variáveis ou redirecionamentos deverão ser ampliadas. Se algum campo permanecer após sua expansão, o primeiro campo será considerado o nome do comando e os campos restantes serão os argumentos do comando.
Os redirecionamentos serão realizados conforme descrito emRedirecionamento.
Cada atribuição de variável deve ser expandida para expansão de til, expansão de parâmetro, substituição de comando, expansão aritmética e remoção de aspas antes de atribuir o valor.
Observe que a etapa 2 é onde ocorre a expansão da variável no comando, mas a etapa 1 nos diz 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}"
ocorrer. Isso explica por que você estava obtendo uma saída vazia.
Para uma discussão mais aprofundada sobre este tópico, consulte as seguintes postagens:
Responder3
Em zsh
, usando uma terceira ${var::=value}
forma dos operadores Bourne ${var=value}
, ${var:=value}
onde a atribuição é incondicional (em oposição a apenas se var não estiver definido/vazio).
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 $x
ter sido configurado no momento da avaliação do código que contém sua expansão.
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 ${var:=value}
operador Bourne depois de definir x
a string vazia.
for i in {1..5}; do x=; echo "${x:=$i}"; done