Converter string "1-5" em array de números em bash

3

Qual é a melhor abordagem para converter a string contendo o intervalo de números em números em si? Esse formato é usado, por exemplo, na definição do intervalo de páginas a serem impressas. Eu defino, eu quero imprimir essas páginas "11-14,17,20" . Agora eu preciso escrever o script bash que irá alterar essa string na matriz de números: 11, 12, 13, 14, 17, 20

Ele deve ser capaz de trabalhar com strings que contenham números (mesmo com mais dígitos) e com ',' e '-'.

srnka
fonte
@Flimzy - Eu tentei usar sed - para dividir a string em linhas sempre que o caracter '-' for encontrado. E, em seguida, olhar para o último número da linha anterior e a primeira linha da linha real e fazer para o ciclo com o incremento ... Um pouco complicado caminho :)
srnka
Sim, sed pode funcionar, com outro código de suporte suficiente. Perl é muito mais poderoso e, portanto, mais conciso (se não for feio) para esse tipo de coisa, se você estiver disposto a usar o perl, como a minha resposta abaixo sugere.
Flimzy

Respostas:

3

Isso faz isso em Perl:

echo 11-14,17,20 | perl -pe 's/(\d+)-(\d+)/join(",",$1..$2)/eg'

Saída:

11,12,13,14,17,20
Flimzy
fonte
2
Eu não posso nem ler isso! Perl realmente mantém seu nome como uma linguagem somente leitura ...
sinni800
Muito boa solução, em uma linha :) E funciona bem para mim! Mas eu não posso usar o perl - o script final deve ser independente do perl.
srnka
Isso funciona mais confiável para o meu sistema embarcado. Eu vou usar isso. Obrigado :)
srnka
Ah, então você pode usar perl depois de tudo? Agradável :)
Flimzy
3

Aqui está uma solução usando awk:

#!/bin/bash

pages="11-14,17,20"

echo $pages | awk 'BEGIN { RS=","; FS="-"; ORS="" }
  NR > 1  { print "," }
  NF > 1  { for (i=$1; i<$2; ++i) { print i "," } print $2; next }
          { print $1 }'

Isso resulta na seguinte saída:

11,12,13,14,17,20

Kusalananda
fonte
Obrigado - é só o que eu estava procurando. Mas como isso funciona? Quando eu quero converter o intervalo 11-14, a saída é: 11,12,13,14,14 O último é dobrado. E eu não posso corrigi-lo.
srnka
Hmm, eu não consigo reproduzir isso sozinho. Eu tentei com o GNU awk 4.0.0 e com o GNU awk 3.1.5, assim como com as instalações padrão do awk no OpenBSD e no Mac OSX, e tudo parece funcionar bem. Qual sistema operacional e awk você está usando?
Kusalananda
@ Srnka, você redigitou meu código e esqueceu o next? Copie e cole em seu lugar.
Kusalananda
Sim, poderia haver um problema - eu usá-lo no Linux incorporado, o awk é uma parte do pacote busybox: BusyBox v1.18.4 (2011-06-22 18:48:08 CEST) multi-call binário.
srnka
Eu tenho um problema com o intervalo que contém o número 9, mas parece ser um bug no awk do busybox .. Só para info :)
srnka
2

Apenas bash:

expand_ranges() {
  local IFS=,
  set -- $1
  for range; do
    case $range in 
      *-*) for (( i=${range%-*}; i<=${range#*-}; i++ )); do echo $i; done ;;
      *)   echo $range ;;
    esac
  done
}
numbers=( $(expand_ranges 11-14,17,20) )

Para sua saída solicitada: IFS=,; echo ${numbers[*]}

glenn jackman
fonte
Não funciona para mim. Apenas escreve o primeiro número e termina. Eu uso o GNU bash, versão 3.2.39 (1)
srnka
isso é porque "números" é na verdade uma matriz, como você solicitou. Usar ${numbers[@]} para imprimir todo o conteúdo da matriz.
glenn jackman
0

Se você tem jot do seu sistema

Exemplo de uso de jot e sed é (não se esqueça nova linha caractere em seqüência de substituição!):

$echo 1-2,3,6-9| sed 's/,/\
/g'|sed -E '/-/s/([0-9]+)-([0-9]+)/- \1 \2/g;s/^([0-9]+)$/- \1 \1/g'| xargs -L 1 jot | rs -t -C,
1,2,3,6,7,8,9

Se você digitar echo jot em vez de "jot" (e remova rs chamada) você deve ver a seguinte saída:

jot - 1 2
jot - 3 3
jot - 6 9
Eir Nym
fonte
Eu editei minha resposta
Eir Nym
0

Essa é uma maneira meio lisp de fazer as coisas (exceto pelo fato de que bash não gosta de listas preguiçosas, então temos que definir um limite superior no número máximo de páginas; aqui eu escolhi 10000)

pages="11-14,17,20"; for ((i=1;i<10000;i++)); do echo -n $i, ; done | cut -d , -f $pages

Com as páginas, você especifica o intervalo de páginas como na outra postagem. O loop for gera uma lista de números de páginas. Desses, o comando cut seleciona os que você quer.

bjoseru
fonte