¿Por qué necesito colocar "hacer" en la misma línea que "para"?

28

1. Resumen

No entiendo, ¿por qué necesito la regla de bashate E010 ?


2. Detalles

Yo uso bashate para .sharchivos linting . Regla E010:

hacer no en la misma línea que para

for bashate:

  • Correcto:

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

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

¿Hay algún argumento válido, por qué lo necesito fory doen la misma línea?


3. No es útil

No puedo encontrar una respuesta a mi pregunta en:

  1. Google
  2. Artículos sobre las mejores prácticas de codificación ( ejemplo )
  3. documentación de bashate . Encuentro única :

    Un conjunto de reglas que ayudan a mantener las cosas consistentes en los bloques de control. Estos se ignoran en las líneas largas que tienen una continuación, porque el desenrollado es algo "interesante"

Саша Черных
fuente
3
El sangrado does definitivamente un poco inusual, pero for ... \ndo\n<indent>body...es extremadamente común e incluso estimaría más que eso for ... ; do. ¿También se queja de eso?
Michael Homer
20
bashatees un corrector de estilo . Los estilos son inherentemente subjetivos. No lo use si no le gusta el estilo artibrary que impone. En su lugar, debe considerar un verificador de sintaxis / error como shellcheck .
meuh
@meuh, gracias, ya uso ShellCheck. mi pregunta: E010- Sólo el estilo imperio o en algunos casos que necesito para y hago en la misma línea, no sólo por razones de estilo.
Саша Черных
10
Nunca hay una obligación de tener sintáctica para y hacer en la misma línea en la cáscara.
meuh
1
@ Саша Черных No es la sangría en sí misma lo que es inusual, es la sangría do. Es como sangrar la llave de apertura de un ify agregar código en esa misma línea en un lenguaje como C. Otro ejemplo, si Python lo permitiera, sería colocar :un ifsangrado en la siguiente línea y agregar código en esa misma línea. Lo hará diferente con líneas adicionales en el cuerpo.
JoL

Respuestas:

60

Sintácticamente, los siguientes dos fragmentos de código son correctos y equivalentes:

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

Este último podría posiblemente puede decir que sea más difícil de leer como doofusca ligeramente el comando en el cuerpo del bucle. Si el bucle contiene múltiples comandos y el siguiente comando está en una nueva línea, la ofuscación se resaltaría aún más:

for f in *
    do cmd1
    cmd2
done

... pero marcarlo como un "error" es, en mi humilde opinión, la opinión personal de alguien más que una verdad objetiva.

En general, casi cualquiera ;puede ser reemplazado por una nueva línea. Ambos ;y newline son terminadores de comando. does una palabra clave que significa "aquí sigue lo que hay que hacer (en este forciclo)".

for f in *; do ...; done

es lo mismo que

for f in *
do
   ...
done

y como

for f in *; do
   ...
done

La razón para usar uno sobre otro es la legibilidad y las convenciones de estilo local / personal.


Opinión personal:

En los encabezados de bucle que son muy largos , creo que puede tener sentido colocar douna nueva línea, como en

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

o

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

Lo mismo ocurre con el thenen una ifdeclaración.

Pero, de nuevo, esto se reduce a las preferencias personales de estilo, o al estilo de codificación que el equipo / proyecto esté usando.

Kusalananda
fuente
Usted dice que "en los encabezados de bucle que son muy largos, puede tener sentido colocar do en una nueva línea". ¿Podría dar una razón para eso? Dado que el encabezado debe ser seguido por do, no veo una razón por la cual ponerlo en la siguiente línea ayudaría a la legibilidad.
Konrad Rudolph
3
@KonradRudolph Mi terminal tiene 80 caracteres de ancho. Si la lista se ajusta, lo convertiré en un conjunto de líneas continuas (como en mi último ejemplo), si casi se ajusta, pondré el do(o then) en una línea por sí mismo (como en el penúltimo ejemplo). Todo esto tiene que ver con las preferencias personales. No me gustan las líneas demasiado largas, en parte porque mi terminal tiene "solo" 80 caracteres de ancho, y en parte porque creo que se ve desordenado. También me resulta difícil analizar líneas muy largas en busca de errores.
Kusalananda
1
Parece innecesario seguir señalando que esta es una opinión. bashate es una guía de estilo, por lo que se basa inherentemente en la opinión.
Barmar
44
@Barmar, tenga en cuenta que la pregunta aquí usa frases como "necesidad de" y "regla", y el archivo readme bashate llama a tener doen una línea separada un "error". Esas son frases bastante estrictas, por lo que vale la pena señalar que, por el contrario, la llamada "regla" es en realidad solo una opinión subjetiva.
ilkkachu
2
@mtraceur Correcto, no cuestiono las preferencias personales. Pero tenga en cuenta que el límite de la columna de legibilidad que cita no se aplica al código. Solo se aplica a la prosa. El movimiento de los ojos es fundamentalmente diferente para la lectura de códigos, por lo que la evidencia de esos estudios no es aplicable aquí. Y en cuanto al uso de razones históricas que ya no se aplican como justificación, bueno, también hemos usado nombres de archivo anteriores a 8.3.
Konrad Rudolph
28

Tenga en cuenta lo que dice en la parte superior de la página:

bashate: un equivalente pep8 para scripts de bash

PEP8 es la guía de estilo para el código Python. Si bien la gente de Python a menudo parece tomar muy en serio sus problemas de estilo [cita requerida] , es solo eso, una guía. Podríamos encontrar ventajas tanto para poner la línea doen la misma línea como forpara ponerla en una línea propia.

Es la misma discusión sobre dónde poner el { en código C al iniciar un ifo whilebloque (o tal).


Algunas líneas a continuación en la documentación, hay otra regla interesante:

E020: Declaración de función no en formato ^function name {$

Supongo que el punto aquí es que el autor de esa guía de estilo piensa que usar la functionpalabra clave es necesario para que el lector reconozca una declaración de función. Sin embargo, function fooes una forma no estándar de definir una función: la forma estándar es foo(). La única diferencia entre los dos (en Bash) es que el otro es más difícil de transferir a un shell estándar, como /bin/shen Debian o Ubuntu.

Por lo tanto, sugeriría tomar cualquiera y todas esas guías de estilo con un grano de sal o tres, y decidir por sí mismo cuál es el mejor estilo. Un buen verificador de estilo permite desactivar algunas verificaciones (bashate tiene bashate -i E010,E011que ignorar esas dos verificaciones). Un excelente verificador de estilo le permitiría modificar las pruebas, por ejemplo, para verificar lo contrario de E020, aquí.

ilkkachu
fuente
12

TL; DR: No es necesario. Es solo la opinión de un tipo.

Como muchos otros, he estado escribiendo forbucles como este durante décadas:

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

Este diseño resalta el hecho de que do ... done rodea el cuerpo del bucle, como las llaves en lenguajes tipo C, y permite que las nuevas líneas separen los componentes de la construcción del bucle.

Por supuesto, también hay guerras religiosas sobre dónde colocar las llaves, por lo que podría decirse que el jurado aún no se ha pronunciado sobre esto. En mi humilde opinión, así es claramente cómo fue diseñado para funcionar; Bourne era aficionado a los delimitadores emparejados, cf. if ... fi, case ... esacy la necesidad de un punto y coma en la versión más corta revela que lo estás haciendo más corto de lo originalmente diseñado. Nada de malo con eso; los avances tecnológicos y muchas cosas que antes parecían una buena idea ahora están mal vistas, como goto. Pero hasta que toda la comunidad de scripting de shell adopte las preferencias de los bashateautores, no tiene que hacer nada.

alexis
fuente
3
Lo fique corresponde a if, y esacetc. proviene de Algol 68. La única razón por la que un forciclo dono termina odes ese odes el nombre de una utilidad estándar existente. Ver en.wikipedia.org/wiki/ALGOL_68C#Algol_68C_and_Unix
Kusalananda
1
Correcto, y los bloques delimitados por palabras clave también se usaron en otros idiomas de la familia Algol, incluido Pascal (que usa, begin ... endetc.).
alexis
2
No había escuchado la historia od, eso es gracioso ... (Aunque, ¿por qué no terminar con bucles rof, entonces?)
alexis
2
@alexis Bueno, como dijo Kusalananda, proviene de Algol 68, ese es el punto clave. Stephen Bourne, el creador del shell Bourne original, fue fuertemente influenciado por ese lenguaje, y es por eso que mantuvo esa sintaxis. Incluso cuando estaba escribiendo en C. Vea el código fuente de bourne shell: minnie.tuhs.org//cgi-bin/utree.pl?file=V7/usr/src/cmd/sh/mac.h Oh, y por el camino, BEGINy ENDse definen allí también.
Sergiy Kolodyazhnyy
1
Todo este estilo de formato también proviene de Algol 68, por cierto. Es FORy DOtambién estaría en líneas separadas, por convención, excepto cuando todo el ciclo encajaría fácilmente en una sola línea.
reinierpost
3

Me sorprende que nadie haya mencionado aún esta sintaxis válida y portátil:

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

Observe cuidadosamente que fory doestán en la misma línea, sin punto y coma. Resultado:

alfa
bravo
charlie
Steven Penny
fuente
Apruebo esta respuesta por incluir una mención de esta sintaxis oscura (pero crítica para el código de shell limpio y portátil en algunos casos), sin embargo, vale la pena explicar explícitamente a la gente que esto cambia los parámetros posicionales, y también, me siento obligado a mencionar que el único "verdaderamente portátil (tm)" la forma es aquella en la que for qy doson en líneas separadas (la forma en este ejemplo fue hace un par de décadas sin apoyo en la cáscara Almquish originales ( ash): in-ulm.de/~mascheck/various/ bourne_args / # endnote )
mtraceur
Si bien el ejemplo en esta respuesta funciona para mí, aplicarlo al ejemplo en la pregunta de OP no funciona.
Scribblemacher
3

Varias buenas respuestas aquí. Siempre tome las guías de estilo con un grano de sal, y en mi humilde opinión y experiencia, preocúpese de una manera moderada a moderada por cualquier comunidad que esté plagada de un dogma excesivo cuando se trata de estilo. Es casi como si creyeran que cuando FINALMENTE obtienen ese último punto, y ese último T cruzado, entonces el software funcionará. A veces se necesita un estilo más que perfecto para eliminar errores ...

Cuando comencé a aprender shell, la mayoría de los scripts y tutes usaban el formato de punto y coma en línea

for i in "$@"; do
    stuff
done

pero como no tenía experiencia, me costó un poco detectar el; y do, ambos esenciales para confirmar la corrección sintáctica. Así que preferí hacer en la siguiente línea por sí solo. Ya no hago eso (juego de palabras)

Además, el comando 'while' es un excelente candidato para un pequeño y agradable truco:

while prep1; prep2; condition; do
    stuff
done

pero cuando los comandos de preparación son un poco voluminosos y / o la condición es compleja, se formatea mejor como

while
    prep1
    prep2
    condition
do
    stuff
done

y luego agregar el do como "; do" es positivamente fugitivo.

Incluso puedes ser más intenso que eso:

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

porque cada enunciado es una expresión. Observe cómo ambos estilos se usan de manera oportunista. Mantenlo limpio y es bastante legible. Compacta o contorsiona en nombre del estilo o "ahorro de espacio" y se convierte en un horrible desastre.

¡Asi que! Teniendo en cuenta los estilos más bien barrocos de los guiones de shell en general, cualquier cosa destinada a aumentar la claridad y el fácil acceso visual a símbolos significativos siempre es algo bueno.

La forma en que 'do' se escribe en línea con el comando es agradable cuando tienes un pequeño comando: no encuentro el 'do' visualmente oculto, ni el comando interno está realmente oscurecido:

for i in whatever
  do stuff
done

Me parece bastante agradable de ver, y tiene análogos con while, if y case:

while condition
  do stuff
end

if condition
  then stuff1
  else stuff2
fi

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

La claridad y el orden general es todo lo que Codd * te pide.

* Edgar F. Codd, padre de la teoría de bases de datos relacionales.


fuente
1
Nunca vi un whilecon una ifcondición antes. ¡Guay!
Joe