Como posso criar um script para contar até cinco?

10

Eu estava tentando criar um script bash muito simples para listar todos os múltiplos de cinco entre 375 e 3500 (375, 380, 385 ...). Uma coisa que eu tentei e não funcionou é:

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

Desisti depois de um tempo e escrevi isso no BASIC em cerca de 15 segundos:

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

Como posso fazer meu programa BASIC em um script bash?

aswine
fonte
isso é melhor no estouro de pilha - Unix e Linux são sobre o sistema operacional, não sobre codificação. Codificação é para stackoverflow.
noɐɹƆzɐɹƆ 18/07/2016

Respostas:

20

Como alternativa, um estilo C tradicional para loop pode ser usado:

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

Talvez isso seja menos claro do que usar seq, mas não gera subprocessos. Embora eu esteja familiarizado com C, não tenho dificuldade em entender isso, mas YMMV.

Muzer
fonte
1
A desvantagem deste método é que ele é menos portátil. O método seq funciona no POSIX sh.
Wouter Verhelst
Ah, o estilo C para loops não existe no POSIX sh? Você aprende algo novo todos os dias ...
Muzer
2
@WouterVerhelst Bem, exceto se você estiver limitado ao POSIX sh, provavelmente não terá o seq, que faz parte dos coreutils do GNU. O busybox tem uma implementação mais limitada, mas fora disso, muitos sistemas embarcados provavelmente não o terão.
23415 Chris Down
1
@Muzer - pelo menos dashnão suporta. @ ChrisDown - Não há razão para que "POSIX sh" tenha que implicar "no seq". Mas sim, com certeza, eu havia ignorado o fato de que seq não é especificado pelo POSIX.
Wouter Verhelst
27

Como você usa a expansão de chaves de qualquer maneira, use totalmente seu recurso:

echo {375..3500..5}

Você também pode usar esta técnica para imprimir cada número em uma linha separada com texto opcional usando, em printfvez de echo, por exemplo:

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

Editar

Conforme apontado por @kojiro no comentário, o Mac OS usa o bash 3 como shell padrão, que não suporta incremento na expressão de sequência da expansão de chaves. Você precisa atualizar para o bash versão 4 ou usar outro shell que suporte isso (por exemplo, zsh recente).

jimmij
fonte
2
Isso não funciona para mim no Mac OS 10.10.3. Ele gera '{375..3500..5}'.
Aswine
6
@ aswine é por isso que você sempre deve mencionar seu sistema operacional ao perguntar. Especialmente porque o OSX tem várias diferenças, de outros UNICEs e do Linux.
terdon
2
Esta não é uma diferença do sistema operacional em si. É uma diferença de versão. O OS X possui apenas BASH 3 OOTB. Você pode instalar um BASH a partir deste século usando quase qualquer gerenciador de pacotes OS X. Eu uso Home Brew, por exemplo.
Kojiro
2
O fato de as expansões falharem é executado diretamente, mas consegue bash -cpraticamente garantir que duas conchas diferentes estejam envolvidas. Diz $SHELL --versionque é bash 3?
dhag 24/07
3
@mikeserv A resposta é muito simples - eu não escrevi isso porque não tinha idéia de qual versão bashdesse recurso foi introduzida. Eu mesmo aprendi com o comentário kojiro. E, por favor, não me compare com o SC, eu realmente não conheço todos os detalhes e histórico de todas as conchas desde o final de 1970. Mais uma vez - Se você espera isso, apenas faça voto negativo.
21815 jimmij
17

Usando SEQ (1)

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

Ou simplesmente:

seq 375 5 3500
Emeric
fonte
4
Ainda mais simples seria largar o loop e usar apenas seq 375 5 3500.
Dj 23/07
11

Seu snippet de loop for não funcionou conforme necessário por dois motivos:

  • (($i += 5))- aqui o $ié expandido para o valor de i. Assim, a expansão será algo como ((375 += 5)), o que não faz sentido (tentar atribuir um número literal a outro número literal). Isso normalmente seria alcançado com ((i += 5))(não $para expandir a variável)
  • O {375..3500}será expandido antes da primeira iteração do loop. Será a lista de números 375 376 ... 3499 3500. Para cada iteração do loop, iserá atribuído a cada um desses números, um por um. Assim, no início de cada iteração, iserá reatribuído para o próximo valor nessa lista, contando-se nas etapas de 1. O ((i += 5))efetivamente não faz nada - ele adiciona 5 a i, mas, em seguida, é reatribuído novamente no início do próxima iteração.

Acho que gosto mais da for (( ; ; ))resposta, mas aqui estão algumas alternativas para você pensar:


Como estamos lidando com múltiplos de 5 e a {a..b..i}expansão não é suportada na versão 3.2.57 (1) do bash (no OS X), podemos fazer uma coisa um pouco misteriosa:

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

Isso demonstra como o bash pode ser usado para criar um produto cartesiano.


Eu acho que, em geral, um loop for é a maneira mais conveniente de fazer isso, mas se você estiver interessado, pode usar um programa while-loop (um pouco mais próximo do seu BASIC):

count=375
while (( count <= 3500 )); do
    echo $count
    (( count += 5 ))
done
Trauma Digital
fonte
1
@jimmij ... o que é um produto cartesiano? você pode reduzir o voto do comentário, se quiser, e eu nem me importo se você me disser. Estou curioso.
mikeserv
1
@mikeserv, você não pode reduzir o voto do comentário: en.wikipedia.org/wiki/Cartesian_product
jimmij
@ jimmij - ainda está tudo bem comigo se você fizer.
Mikeerv
@jimmij O diabo você está falando? Um produto cartesiano é um conjunto de tuplas de todas as combinações possíveis de elementos de dois conjuntos (que podem ou não ser o mesmo conjunto). Mais coloquialmente, são apenas "todas as combinações possíveis" de dois grupos de coisas. Eu vejo apenas um conjunto. Como existe um produto cartesiano aqui?
jpmc26
1
@ jpmc26 Você está certo, são "todas as combinações possíveis", então vamos plotar dois eixos - primeiro escreva todos os números de 38até 349. No segundo, apenas dois números: 0e 5. Agora vamos criar pares ordenados desses todas as combinações - você pode escrever-los como um conjuntos: {38,0}, {38,5}... {349,5}, ou simplesmente remover símbolos redundantes {, ,e }e obter ... novos números 380... 3495.
jimmij
5

Embora exista, é claro, um aplicativo para isso ( seq 375 5 3500), existem várias maneiras de fazer isso na linha de comando. Enquanto o mais rápido e o mais simples serão usados seq, aqui estão algumas outras opções:

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
fonte
Nit pick: $(($i % 5))pode ser escrito $((i % 5)).
G-Man diz 'Reinstate Monica'
4

POSIXly:

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

...ou...

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

Não sei por que você faria de outra maneira. Exceto, é claro ...

seq 375 5 3500

... ou com dc:

echo '370[5+pd3500>p]splpx'|dc
mikeserv
fonte
Estou curioso para saber como o dccódigo funciona. É certamente mais intrigante do que usar seq.
Dj 24/07
1
@ dhag - escreve um pequeno loop. ele coloca so [barbante ]no topo da pmatriz, depois o lcoloca de volta no topo da pilha e o xfaz como uma macro. Na macro, empurramos 5a pilha e, em seguida, a colocamos e a segunda de cima e +elas, substituindo os dois valores da pilha pela soma, que é pcriada e daumentada antes de 3500ser empurrada na pilha, quando a colocamos e a bobagem que acabamos de criar para comparação >. Se 3500for maior, carregamos e executamos como macro a string armazenada na pmatriz (nossa macro atual) ou, se não for quebrada.
mikeserv
3

Se você está preso no Bash 3:

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

e se você preferir awk:

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

Eu não sabia sobre a expansão da cinta - isso é muito legal.

bonh
fonte