As condições do loop interno devem depender da execução do loop externo

3

Como posso construir um loop interno de tal forma que a condição de loop depende do loop externo executado?

Minha situação provavelmente não importa para o código que estou procurando, mas aqui está: tenho um rastreador que desejo executar ao alterar URLs, onde o URL depende de dois parâmetros. O primeiro é o ano e o segundo são páginas, enquanto o intervalo de páginas varia de ano para ano.

Aqui está o que eu tentei até agora

#!/bin/bash

numbers2004={625..721}
numbers2005={723..823}

for year in 2004 2005
do
  for number in numbers$year
  do
    echo "$year $number"
  done
done

Deveria me dar

2004 625
2004 626
...
2004 720
2004 721
2005 723
2005 724
...
2005 822
2005 823
    
por MERose 17.12.2014 / 01:40

4 respostas

2

Com a versão atual do bash:

#!/bin/bash

declare -A numbers    # declare associative array
printf -v numbers[2004] "%s " {625..721}
printf -v numbers[2005] "%s " {723..823}

for year in 2004 2005
do
  for number in ${numbers[$year]}
  do
    echo "$year $number"
  done
done
    
por 17.12.2014 / 02:20
1

Aqui está uma variante da resposta de Cyrus que usa parâmetro indireto . No entanto, como diz o link, as abordagens baseadas em array devem ser preferidas em relação ao uso de indireção, já que tal indireto é um primo próximo de eval , que deve ser evitado sempre que possível.

(reduzi os intervalos dos números daqueles dados no OP apenas para deixar a saída um pouco menor).

#!/usr/bin/env bash

printf -v numbers2004 "%d " {625..635}
printf -v numbers2005 "%d " {723..733}

for year in 2004 2005
do
    numbers="numbers$year"
    for number in ${!numbers}
    do
        printf "%s %s\n" "$year" "$number"
    done
done

saída

2004 625
2004 626
2004 627
2004 628
2004 629
2004 630
2004 631
2004 632
2004 633
2004 634
2004 635
2005 723
2005 724
2005 725
2005 726
2005 727
2005 728
2005 729
2005 730
2005 731
2005 732
2005 733
    
por 17.12.2014 / 07:54
0

Você também pode conseguir isso com uma indireta variável:

#!/bin/bash

numbers2004="$(printf "%s " {625..721})"
numbers2005="$(printf "%s " {723..823})"

for year in 2004 2005
do
  for number in $(eval echo \$numbers$year)
  do
    echo "$year $number"
  done
done
    
por 17.12.2014 / 03:41
0

Você precisa de eval para fazer do jeito que está tentando fazer ...

numbers2004={625..721}
numbers2005={723..823}

for year in 2004 2005
do
  eval 'eval "for number in '"\$numbers$year"'
  do
    echo \"\$year \$number\"
  done"'
done

... que imprime ...

2004 625
...
2004 721
2005 723
...
2005 823

Mas isso é uma maneira horrível de fazê-lo - e não apenas porque a citação é um pesadelo -, mas provavelmente porque o seu shell faz todo o trabalho duas vezes . Primeiro, ele deve gerar todos os {brace..expanded} iterables antes e depois iterar sobre cada um deles.

Em vez disso, talvez:

y=4 n=623 c=721
while  [ "$((y+=$c<(c+=102*(c<(n+=1<<(c==n))))))" -lt 6  ] 
do    echo "200$y $n"
done

... que imprime o mesmo nas versões recentes de busybox ash , ksh93 , dash , yash , bash , zsh , mksh e posh .

A expressão aritmética pode ser levada um pouco mais para a maioria daqueles avaliando partes dela somente quando necessário, como ...

y=4 n=623 c=721
while [ "$((c<(n+=1)?(y+=(n+=1)<(c+=102)):y))" -lt 6 ] 
do echo "200$y $n"
done

... que funciona de forma idêntica em todos os shells mencionados, com exceção de busybox . Parece que busybox sempre avalia todos os lados da instrução if expr ? true : false ternário e, portanto, falha em iterar conforme o esperado.

    
por 17.12.2014 / 03:42