¿Cómo puedo hacer un guión para contar por cinco?

10

Estaba tratando de hacer un script bash muy simple para enumerar todos los múltiplos de cinco entre 375 y 3500 (375, 380, 385 ...). Una cosa que probé y no funcionó es:

for i in {375..3500}
do
        echo $i
        (($i += 5))
done

Me di por vencido después de un tiempo y escribí esto en BASIC en unos 15 segundos:

10 count = 375
20 print count
30 count = count+5
40 if count < 3500 then goto 20

¿Cómo puedo hacer mi programa BASIC en un script bash?

llorón
fuente
esto es mejor en el desbordamiento de pila: Unix y Linux se trata del sistema operativo, no de la codificación. La codificación es para stackoverflow.
noɥʇʎԀʎzɐɹƆ

Respuestas:

20

Alternativamente, se puede usar un estilo C tradicional para el bucle:

for ((i=375; i<=3500; i+=5)); do
    echo $i
done

Esto es quizás menos claro que usar seq, pero no genera ningún subproceso. Aunque como estoy familiarizado con C, no tendría ninguna dificultad para entender esto, pero YMMV.

Muzer
fuente
1
La desventaja de este método es que es menos portátil. El método seq funciona en POSIX sh.
Wouter Verhelst
Oh, ¿el estilo C para bucles no existe en POSIX sh?
Aprendes
2
@WouterVerhelst Bueno, excepto si está limitado a POSIX sh, es probable que no tenga seq, que es parte de los coreutils de GNU. busybox tiene una implementación más limitada, pero fuera de eso, es probable que muchos sistemas integrados no la tengan en absoluto.
Chris Down
1
@ Muzer: al menos dashno lo admite. @ChrisDown - No hay ninguna razón por qué "POSIX sh" tiene dar a entender "no ss". Pero sí, claro, había pasado por alto el hecho de que seq no está especificado por POSIX.
Wouter Verhelst
27

Como usas la expansión de llaves de todos modos, haz uso de su función por completo:

echo {375..3500..5}

También puede usar esta técnica para imprimir cada número en una línea separada con texto opcional usando en printflugar de echo, por ejemplo:

$ printf "Number %s is generated.\n" {375..3500..5}
Number 375 is generated.
Number 380 is generated.
Number 385 is generated.
...

Editar

Como señaló @kojiro en el comentario, Mac OS usa bash 3 como shell predeterminado, que no admite el incremento en la expresión de secuencia de expansión de llaves. Debe actualizar a bash versión 4 o utilizar otro shell que lo admita (por ejemplo, zsh reciente).

jimmij
fuente
2
Esto no funciona para mí en Mac OS 10.10.3. Produce '{375..3500..5}'.
aswine
66
@aswine es por eso que siempre debes mencionar tu sistema operativo cuando preguntes. Especialmente porque OSX tiene una serie de diferencias tanto con respecto a otros UNICE como a Linux.
terdon
2
Esta no es una diferencia del sistema operativo per se. Es una diferencia de versión. OS X solo tiene BASH 3 OOTB. Puede instalar un BASH de este siglo utilizando casi cualquier administrador de paquetes de OS X. Yo uso Home Brew, por ejemplo.
kojiro
2
El hecho de que las expansiones fallen se ejecuta directamente, pero tiene éxito en bash -ccasi todas las garantías de que están involucrados dos proyectiles diferentes. ¿ $SHELL --versionDice que es bash 3?
dhag
3
@mikeserv La respuesta es muy simple: no escribí eso porque no tenía idea de qué versión de bashesta característica se introdujo. Lo aprendí yo mismo del comentario de kojiro. Y, por favor, no me comparen con SC, realmente no conozco todos los detalles y la historia de todos los proyectiles desde finales de 1970. Una vez más, si lo esperan, denle un voto negativo.
jimmij
17

Usando SEQ (1)

for i in $(seq 375 5 3500)
do
    echo $i
done

O simplemente:

seq 375 5 3500
Emeric
fuente
44
Aún más simple sería abandonar el ciclo y simplemente usarlo seq 375 5 3500.
dhag
11

Su fragmento de bucle for no funcionó como lo requiere por dos razones:

  • (($i += 5))- aquí $ise expande al valor de i. Por lo tanto, la expansión será algo así ((375 += 5)), lo que no tiene sentido (intentar asignar un número literal a otro número literal). Esto normalmente se lograría con ((i += 5))(no $para expandir la variable)
  • El {375..3500}se expandirá antes de la primera iteración del bucle. Será la lista de números 375 376 ... 3499 3500. Para cada iteración del ciclo, ise asignará a cada uno de estos números, uno por uno. Por lo tanto, al comienzo de cada iteración, ise reasignará al siguiente valor en esa lista, contando en los pasos de 1. ((i += 5))Efectivamente no hace nada: agrega 5 a i, pero luego me reasignan nuevamente al comienzo del siguiente iteración

Creo que me gusta más la for (( ; ; ))respuesta, pero aquí hay algunas alternativas para que pienses:


Dado que estamos lidiando con múltiplos de 5, y la {a..b..i}expansión no es compatible con la versión 3.2.57 (1) de bash (en OS X), entonces podemos hacer esto algo arcano en su lugar:

for i in 375 {38..349}{0,5} 3500; do
    echo $i
done

Esto demuestra cómo se puede usar bash para crear un producto cartesiano.


Creo que, en general, un bucle for es la forma más conveniente de hacer esto, pero si está interesado, puede usar un programa while-loop (un poco más cercano a su BASIC):

count=375
while (( count <= 3500 )); do
    echo $count
    (( count += 5 ))
done
Trauma digital
fuente
1
@jimmij ... ¿qué es un producto cartesiano? puedes votar el comentario si quieres, y ni siquiera me importará si me lo cuentas. Soy curioso.
mikeserv
1
@mikeserv no puede rechazar el comentario: en.wikipedia.org/wiki/Cartesian_product
jimmij
@ jimmij - todavía está bien conmigo si lo haces.
mikeserv
@jimmij ¿De qué demonios estás hablando? Un producto cartesiano es un conjunto de tuplas de cada combinación posible de elementos de dos conjuntos (que pueden o no ser el mismo conjunto). Más coloquialmente, es solo "todas las combinaciones posibles" de dos grupos de cosas. Solo veo un conjunto. ¿Cómo hay un producto cartesiano aquí?
jpmc26
1
@ jpmc26 Tiene razón, es "todas las combinaciones posibles", así que grafiquemos dos ejes: primero escriba todos los números desde 38hasta 349. En el segundo solo dos números: 0y 5. Ahora vamos a crear pares ordenados de esas combinaciones de todos - se puede escribir como un conjuntos: {38,0}, {38,5}... {349,5}, o simplemente quitar los símbolos redundantes {, ,y }y obtener nuevos números ... 380... 3495.
jimmij
5

Si bien, por supuesto, hay una aplicación para eso ( seq 375 5 3500), hay varias formas de hacerlo desde la línea de comandos. Si bien lo más rápido y sencillo será simplemente usar seq, aquí hay algunas otras opciones:

for i in {375..3500}; do [[ (($i % 5)) -eq 0 ]] && echo $i; done

i=370; while [ $i -le 3500 ]; do printf "%s\n" $((i+=5)); done

perl -le '$i=shift;while($i<=$ARGV[0]){print $i; $i+=5; }' 375 3500
perl -le 'map{print $_ + 5} 370..3495'

awk 'BEGIN{ for(i=375;i<=3500;i+=5){print i}}'
terdon
fuente
Nit pick: $(($i % 5))se puede escribir $((i % 5)).
G-Man dice 'Restablece a Monica' el
4

POSIXY:

i=370
while [ 3500 -gt "$i" ]
do    echo "$((i+=5))"
done

...o...

echo 'for(x=370;x<=3500;x+=5)x' |bc

No sé por qué lo harías de otra manera. Excepto, por supuesto ...

seq 375 5 3500

... o con dc:

echo '370[5+pd3500>p]splpx'|dc
mikeserv
fuente
Tengo curiosidad por saber cómo funciona el dccódigo. Ciertamente es más intrigante que usar seq.
dhag
1
@dhag: escribe un pequeño bucle. que saves la [cadena ]a la parte superior de la pmatriz, entonces lOADS de nuevo en la parte superior de la pila y e xecutes como una macro. Dentro de la macro, empujamos 5la pila, luego la sacamos y la segunda desde arriba y +los reemplazamos, ambos valores de la pila con su suma, que se penjuaga y se dduplica antes de que 3500se empuje en la pila, cuando la sacamos y el engaño que acabamos de hacer. comparación >. Si 3500es mayor, cargamos y ejecutamos como macro la cadena almacenada en la pmatriz (nuestra macro actual) , o si no se rompe.
mikeserv
3

Si estás atrapado en Bash 3:

echo {375..3500} | tr ' ' '\n' | sed -n 'p;n;n;n;n'

y si lo prefieres awk:

echo {375..3500} | tr ' ' '\n' | awk '!((NR-1)%5)'

No sabía sobre la expansión de aparatos ortopédicos, eso es realmente genial.

bonh
fuente