Estou tentando entender a sintaxe do loop for nas regras gramaticais do shell POSIX :
for_clause : For name do_group
| For name sequential_sep do_group
| For name linebreak in sequential_sep do_group
| For name linebreak in wordlist sequential_sep do_group
O último (praticamente o padrão) e os dois primeiros fazem sentido, citando posix:
Omitindo:
in word...
será equivalente a:in "$@"
então os dois primeiros estão apenas repetindo os argumentos do script de shell.
Eu simplesmente não entendo como um loop for que consiste em for name in; do echo $name; done
faz sentido. Achei que ainda estava apenas repetindo os argumentos do script de shell, mas criar um script pequeno não parece ser o caso. O loop for parece ser ignorado. Então, qual é o propósito dessa terceira variação?
É que isso
wordlist
é definido como:Portanto, uma ou mais palavras, mas o
for var in ...; do ...; done
loop pode passar por zero ou mais palavras, portanto, precisaFor name linebreak in sequential_sep do_group
cobrir o caso de zero palavras .Tendo
wordlist
definido como zero ou mais palavras :Provavelmente teria sido menos confuso.
Se você olhar para SUSv2 , havia:
Significando que uma lista vazia não era permitida. Parece que o que aconteceu foi que no SUSv3, eles corrigiram essa omissão adicionando:
Em vez de mudar a definição de
wordlist
.Observe que estamos falando da gramática da linguagem shell aqui, portanto não se trata
for i in $var; do ...; done
defor i in $(some cmd); do ...; done
onde$var
/$(some cmd)
expansões podem resultar em uma lista vazia. Esses são$var
,$(some cmd)
cada um, um token WORD na formalização POSIX da linguagem shell.Estamos falando de
for i in; do ...; done
, onde literalmente não há PALAVRA entre oin
e odo
.Concordo que o código não é particularmente útil. Há poucos motivos para escrever um loop que faça um loop explicitamente sobre nada. Algumas razões que se poderia pensar:
Código gerado:
Ou qualquer coisa que gere
sh
scripts e construa umfor
loop sobre argumentos explícitos.Ou para comentar algum código:
Embora algo como:
Seria mais idiomático.
Não encontrei a solicitação de mudança que levou à modificação do padrão, mas suponho que eles o alteraram principalmente porque todas as implementações de shell existentes permitiam
for i in; do ...; done
, então não fazia sentido proibi-lo no padrão.Você descobrirá que ele não permite
if then ...; else ...; fi
nemif; then ...; else ...; fi
nemif ...; then;else ...; fi
¹ (mas permiteif $empty; then $empty; else $empty; fi
obviamente), mesmo que não haja uma boa razão para proibi-lo, porque a maioria das implementações (zsh
sendo uma exceção notável), começando com o shell Bourne que o introduziu e o Korn shell no qual ash
especificação POSIX é baseada, na prática, engasga com ele.¹ Na verdade, no shell Bourne que não possuía a
!
palavra-chave para negar o status de saída de um pipeline, era necessário escreverif cmd; then :; else command if cmd fails; fi
no lugar deif ! cmd; then command if cmd fails; fi
, ou seja, usar um:
comando nulo explícito nathen
parte, pois o shell Bourne não permitia umthen
parte vazia .O raciocínio para esse comportamento é melhor ilustrado com um exemplo. Suponha que você tenha uma variável nomeada
${items}
e precise chamar uma função em cada palavra dessa variável, mas ela pode ser avaliada como uma string vazia em alguns casos.Com comportamento compatível com POSIX, você pode simplesmente usar:
Aqui, não importa se é
${items}
avaliado como uma string vazia ou não, porque se isso acontecer, o loop não terá nada para repetir e será simplesmente ignorado.Mas se o shell tratasse o caso de uma lista de palavras vazias de maneira diferente (ou não, ou equivalente a um dos formulários sem o
in
), então você precisaria (no mínimo):Portanto, esse comportamento economiza (no mínimo) um nível de recuo e uma verificação condicional extra no caso relativamente comum de ter que iterar sobre uma lista potencialmente vazia de itens de comprimento desconhecido. Muitas outras linguagens que possuem loops for que iteram sobre uma coleção de itens (como Python ou JavaScript) também se comportam exatamente da mesma maneira, apenas exigem explicitamente que uma variável (ou literal) seja fornecida ao loop, enquanto o shell script faz não parece devido à forma como uma lista literal de itens a serem repetidos é definida (ou seja, uma lista vazia é apenas uma string vazia sem aspas).