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"


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.


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.


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.


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

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.


1
Nunca vi um whilecom uma ifcondição antes. Legal!
Joe
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.