Por que preciso colocar "do" na mesma linha que "for"?

28

1. Resumo

Não entendo, por que preciso da regra de bashate E010 .


2. Detalhes

Eu uso o bashate para linting de .sharquivos. Regra E010:

fazer não na mesma linha para

for bashate:

  • Corrigir:

    #!/bin/bash
    for f in bash/*.sh; do
        sashacommand "$f"
    done
  • Erro:

    #!/bin/bash
    for f in bash/*.sh
        do sashacommand "$f"
    done

Existe algum argumento válido, por que eu preciso fore dona mesma linha?


3. Não é útil

Não consigo encontrar uma resposta para minha pergunta em:

  1. Google
  2. Artigos sobre as melhores práticas de codificação ( exemplo )
  3. documentação de base . Eu acho apenas :

    Um conjunto de regras que ajudam a manter as coisas consistentes nos blocos de controle. Eles são ignorados em longas filas que têm uma continuação, porque desenrolar isso é meio "interessante"

Саша Черных
fonte
3
O recuo doé definitivamente um pouco incomum, mas for ... \ndo\n<indent>body...é extremamente comum e eu ainda estimaria mais do que isso for ... ; do. Ele também reclama disso?
Michael Homer
20
bashateé um verificador de estilo . Os estilos são inerentemente subjetivos. Não use se você não gosta do estilo de biblioteca de arte que ele impõe. Em vez disso, você deve considerar um verificador de sintaxe / erros como o shellcheck .
meuh
@ meuh, obrigado, eu já uso ShellCheck. minha pergunta: E010- única estilo regra ou , em alguns casos que eu preciso para e fazer na mesma linha, não só por razões de estilo.
Саша Черных
10
Nunca há uma obrigação sintática ter para e fazer na mesma linha no shell.
Meu
1
@ Саша Черных Não é o recuo por si só que é incomum, é o recuo de do. É como recuar a chave de abertura de um ife adicionar código nessa mesma linha em uma linguagem como C. Outro exemplo, se o python permitir, seria colocar :um de um ifrecuo na próxima linha e adicionar código nessa mesma linha. Vai fazer a diferença com linhas adicionais no corpo.
JoL

Respostas:

60

Sintaticamente, os dois trechos de código a seguir estão corretos e equivalentes:

for f in bash/*.sh; do
    sashacommand "$f"
done
for f in bash/*.sh
    do sashacommand "$f"
done

Esta última pode-se possivelmente ser considerado mais difícil de ler como doobfuscates ligeiramente o comando no corpo do ciclo. Se o loop contiver vários comandos e o próximo comando estiver em uma nova linha, a ofuscação será destacada ainda mais:

for f in *
    do cmd1
    cmd2
done

... mas sinalizá-lo como um "erro" é IMHO a opinião pessoal de alguém, e não uma verdade objetiva.

Em geral, quase todos ;podem ser substituídos por uma nova linha. Ambos ;e nova linha são terminadores de comando. doé uma palavra-chave que significa "aqui segue o que precisa ser feito (neste forloop)".

for f in *; do ...; done

é o mesmo que

for f in *
do
   ...
done

e como

for f in *; do
   ...
done

A razão para usar um sobre o outro é a legibilidade e convenções de estilo local / pessoal.


Opinião pessoal:

Em cabeçalhos de loop muito longos , acho que pode fazer sentido colocar douma nova linha, como em

for i in animals people houses thoughts basketballs bees
do
    ...
done

ou

for i in        \
    animals     \
    people      \
    houses      \
    thoughts    \
    basketballs \
    bees
do
    ...
done

O mesmo vale para o thenem uma ifdeclaração.

Mas, novamente, isso se resume às preferências pessoais de estilo ou a qualquer estilo de codificação que a equipe / projeto esteja usando.

Kusalananda
fonte
Você diz que “nos cabeçalhos de loop muito longos, pode fazer sentido colocar uma nova linha em ação”. Você poderia fornecer uma razão para isso? Dado que o cabeçalho deve ser seguido do, não vejo uma razão pela qual colocá-lo na próxima linha ajude a legibilidade.
Konrad Rudolph
3
@KonradRudolph Meu terminal tem 80 caracteres de largura. Se a lista for agrupada, tornarei um conjunto de linhas continuadas (como no meu último exemplo); se quase agrupar, colocarei o do(ou then) em uma linha por conta própria (como no segundo para o último exemplo). Tudo isso tem a ver com preferências pessoais. Não gosto de linhas muito longas, em parte porque meu terminal tem "apenas" 80 caracteres de largura e em parte porque acho que parece desarrumado. Também acho difícil analisar linhas muito longas quanto a erros.
Kusalananda
1
Parece desnecessário ressaltar que essa é uma opinião. O bashate é um guia de estilo, por isso é inerentemente baseado em opiniões.
Barmar 14/05
4
@ Barmar, observe que a pergunta aqui usa frases como "need to" e "rule", e o leia-me com base em chamadas chama o fato de ter doem uma linha separada um "erro". Aqueles são bastante frases rigorosos, por isso não parece valer a pena salientar que, muito pelo contrário, a chamada "regra" é na verdade apenas uma opinião subjectiva.
Ilkkachu
2
@mtraceur Certo, não estou questionando as preferências pessoais. Mas lembre-se de que o limite da coluna de legibilidade que você cita não se aplica ao código. Isso só se aplica à prosa. O movimento dos olhos é fundamentalmente diferente para a leitura do código; portanto, as evidências desses estudos não são aplicáveis ​​aqui. E quanto ao uso de razões históricas que não se aplicam mais como justificativa, também usamos nomes de arquivos 8.3 anteriores.
Konrad Rudolph
28

Observe o que diz na parte superior da página:

bashate: um equivalente pep8 para scripts bash

O PEP8 é o Guia de Estilo para Código Python. Embora o pessoal do Python pareça levar seus problemas de estilo muito a sério [citação necessário] , é apenas isso, um guia. Poderíamos criar vantagens tanto para colocar o domesmo na linha quanto forpara colocar na linha própria.

É a mesma discussão de onde colocar o {código C ao iniciar um ifou whilebloco (ou algo assim).


Algumas linhas abaixo na documentação, há outra regra interessante:

E020: Declaração de função fora do formato ^function name {$

Suponho que o argumento aqui é que o autor desse guia de estilo pensa que o uso da functionpalavra-chave é necessário para o leitor reconhecer uma declaração de função. No entanto, function fooé uma maneira não padrão de definir uma função: a maneira padrão é foo(). A única diferença entre os dois (no Bash) é que o outro é mais difícil de portar para um shell padrão, como /bin/shno Debian ou Ubuntu.

Então, eu sugiro que você tome todos e quaisquer guias de estilo com um grão de sal ou três e decida por si mesmo qual é o melhor estilo. Um bom verificador de estilo permite desativar algumas verificações (o bashate deve bashate -i E010,E011ignorar essas duas verificações.) Um excelente verificador de estilo permitiria modificar os testes, por exemplo, verificar o oposto de E020 aqui.

ilkkachu
fonte
12

TL; DR: Você não precisa. É apenas a opinião de um cara.

Como muitos outros, eu tenho escrito forloops há décadas:

for F in arg1 arg2 arg*
do
    somecommand "$F"
done

Esse layout destaca o fato de do ... doneenvolver o corpo do loop, como chaves em linguagens do tipo C, e permite que as novas linhas separem os componentes da construção do loop.

É claro que existem guerras religiosas sobre onde colocar os aparelhos também, então você pode dizer que o júri ainda está envolvido. IMHO, é claramente como isso foi projetado para funcionar; Bourne gostava de delimitadores correspondentes, cf. if ... fi,, case ... esace a necessidade de ponto e vírgula na versão mais curta revela que você está diminuindo o tamanho do que foi originalmente projetado. Nada de errado com isso; avanços tecnológicos e muitas coisas que antes pareciam uma boa idéia agora são desaprovadas goto. Mas até que toda a comunidade de scripts de shell adote as preferências do bashate(s) autor (es), você não precisa fazer nada.

alexis
fonte
3
O fique corresponde a if, e esacetc., vem do Algol 68. A única razão pela qual um forloop donão termina odé que odé o nome de um utilitário padrão existente. Veja en.wikipedia.org/wiki/ALGOL_68C#Algol_68C_and_Unix
Kusalananda
1
Blocos à direita e delimitados por palavras-chave também foram usados ​​em outros idiomas da família Algol, incluindo Pascal (que usa begin ... endetc.).
Alexis
2
Não tinha ouvido a história sobre od, isso é engraçado ... (Embora, por que não simplesmente acabar para-loops com rof, então?)
alexis
2
@alexis Bem, como Kusalananda disse, vem do Algol 68, esse é o ponto principal. Stephen Bourne, o criador do shell Bourne original, foi fortemente influenciado por esse idioma, e foi por isso que ele se ateve a essa sintaxe. Mesmo quando ele estava escrevendo em C. Veja o código-fonte do bourne shell: minnie.tuhs.org//cgi-bin/utree.pl?file=V7/usr/src/cmd/sh/mac.h Ah, e por o caminho, BEGINe ENDsão definidos lá também.
Sergiy Kolodyazhnyy 13/05/19
1
Todo esse estilo de formatação também vem do Algol 68, a propósito. É FORe DOtambém estaria em linhas separadas, por convenção, exceto quando o loop inteiro caberia facilmente em uma única linha.
Reinierpost
3

Estou surpreso que ninguém tenha mencionado essa sintaxe válida e portátil ainda:

set alfa bravo charlie
for q do
  echo "$q"
done

Observe com cuidado que fore doestão na mesma linha, sem ponto e vírgula. Resultado:

alfa
bravo
charlie
Steven Penny
fonte
Eu aprovo esta resposta por incluir uma menção a essa sintaxe obscura (mas crítica para o código de shell limpo e portátil em alguns casos); no entanto, vale a pena explicar explicitamente às pessoas que isso prejudica os parâmetros posicionais e também me sinto obrigado a mencionar que a única "verdadeiramente portátil (tm)" forma é aquele onde for qe doestão em linhas separadas (a forma neste exemplo foi sem suporte um par de décadas atrás na casca Almquish original ( ash): in-ulm.de/~mascheck/various/ bourne_args / # endnote )
mtraceur
Enquanto o exemplo nesta resposta funciona para mim, aplicá-lo ao exemplo na pergunta do OP não funciona.
Scribblemacher
3

Várias boas respostas aqui. Sempre leve guias de estilo com um pouco de sal, e IMHO e experiência, preocupe-se levemente a moderadamente com qualquer comunidade que esteja repleta de dogmas excessivos quando se trata de estilo. É quase como se eles acreditassem que, quando FINALMENTE chegarem ao último ponto que eu pontuei e esse último T cruzado, o software funcionará. Às vezes, é preciso um estilo mais que perfeito para remover os bugs ...

Quando comecei a aprender shell, a maioria dos scripts e tutes por aí usavam o formato de ponto-e-vírgula em linha

for i in "$@"; do
    stuff
done

mas como eu era inexperiente, tive dificuldade em encontrar o; e faça - ambos essenciais para confirmar a correção sintática. Então, eu preferi o fazer na próxima linha por si só. Eu não faço mais isso (trocadilhos)

Além disso, o comando 'while' é um excelente candidato a um pequeno truque:

while prep1; prep2; condition; do
    stuff
done

mas quando os comandos de preparação são um pouco volumosos e / ou a condição é complexa, é melhor formatado como

while
    prep1
    prep2
    condition
do
    stuff
done

e, em seguida, anexar o do como "; do" é positivamente incorreto.

Você pode até ficar mais intenso que isso:

while
    if con1; then
      cond2
    elif cond3; then
      cond4
    elif cond5; then
      cond6
    else
      cond7
    fi
do
    stuff
done

porque toda afirmação é uma expressão. Observe como os dois estilos são usados ​​oportunisticamente. Mantenha-o limpo e é bastante legível. Compactar ou contorcê-lo em nome do estilo ou "economizar espaço" e torna-se uma bagunça horrível.

Tão! Dado o estilo bastante barroco dos scripts de shell em geral, qualquer coisa que vise aumentar a clareza e o fácil acesso visual a símbolos significativos é sempre uma coisa boa.

A forma em que 'do' é escrita de acordo com o comando é agradável quando você tem um pequeno comando - não acho o 'do' visualmente oculto, nem o comando interno é realmente obscuro:

for i in whatever
  do stuff
done

Acho isso bastante agradável de se ver, e tem análogos com while, if e case:

while condition
  do stuff
end

if condition
  then stuff1
  else stuff2
fi

case $blah in
  a) stuff1;;
  b) stuff2;;
  c) stuff3;;
  *) stuffInfinity;;
esac

Clareza e arrumação geral é tudo o que o Codd * pede de você.

* Edgar F. Codd, pai da teoria do banco de dados relacional.


fonte
1
Nunca vi um whilecom uma ifcondição antes. Legal!
Joe