Como posso usar $ variable na expansão de uma sequência de chaves?


33

Eu quero usar $var inuma expansão de chave de concha com um intervalo, no bash. Simplesmente colocar {$var1..$var2}não funciona, então fui "lateral" ...

O seguinte funciona, mas é um pouco desagradável.

# remove the split files
echo rm foo.{$ext0..$extN} rm-segments > rm-segments
source rm-segments

Existe uma maneira mais "normal"?

Respostas:


26

Você pode tentar:

eval rm foo.{$ext0..$extN}

Não tenho certeza se esta é a melhor resposta, mas certamente é uma.


Durrh! :( Eu não podia ver o óbvio ... Thanks :)
Peter.O

4
No bash, a expansão {} ocorre antes da expansão $, então não acho que exista outra maneira de fazer isso além de usar eval ou algum outro truque para causar duas passagens pela expressão.
mattdm

6
Eu apenas "peguei"! ... Não há expansão entre chaves{$one..$three} , porque não é uma forma válida de expansão entre chaves, que neste caso espera números inteiros ... Ela só se torna uma forma válida após a expansão $ var, que evalpassa pelo mesmo processo para gerar uma expansão normal da sequência de chaves ... 1 2 3 QED;) ... Resumo: a simples presença de um par de chaves não aciona a expansão de chaves ... apenas um formulário válido a aciona .
precisa saber é o seguinte

@fred: Welcome :-)
asoundmove

Ao usar uma variável como a={0..9}; echo $a;não há extensão entre chaves . Usando eval, ele funciona. Então eu acho que a explicação do @ mattdm é boa.
Dr0i

20

Como você já percebeu, {1..3}expande-se para 1 2 3, mas {foo..bar}ou {$foo..$bar}não expansão cinta gatilho, e este último é posteriormente expandido para substituir $fooe $barpor seus valores.

Um fallback no GNU (por exemplo, Linux não incorporado) é o seqcomando.

for x in `seq $ext0 $extN`; do rm foo.$x; done

Outra possibilidade. se foo.não contém nenhum caractere especial do shell, é

rm `seq $ext0 $extN | sed 's/^/foo./'`

A solução mais simples é usar o zsh, onde rm foo.{$ext0..$extN}faz o que você deseja.


Obrigado pelas opções .. É bom vê-los todos agrupados .. Vou ficar com bash e eval ... eval é simples o suficiente agora que sei o que está acontecendo nos bastidores; então não é mais um problema. Não há necessidade de mudar um bom cavalo por causa do piloto:) ...
Peter.O

1

Enquanto as outras respostas discutem o uso evale seq, em bash, você pode usar um forloop de estilo C tradicional em um contexto aritmético. As variáveis ext0e extNsão expandidas dentro da ((..))execução do loop para o intervalo definido.

for (( idx = ext0; idx <= extN; idx++ )); do
    [[ -f "$foo.$idx" ]] || { printf "file %s does not exist" "$foo.$idx" >&2 ; continue }
    rm "$foo.$idx"
done

Se você estiver procurando uma maneira ideal e evitar vários rmcomandos, poderá usar um espaço reservado temporário para armazenar os resultados do nome do arquivo e chamar rmde uma só vez.

 results=()
 for (( idx = ext0; idx <= extN; idx++ )); do
     [[ -f "$foo.$idx" ]] || { printf "file %s does not exist" "$foo.$idx" >&2 ; continue }
     results+=( "$foo.$idx" )
 done

e agora chame o rmcomando na matriz expandida

 rm -- "${results[@]}"

0
    function f_over_range {
        for i in $(eval echo {$1..$2}); do
            f $i
        done
    }

    function f {
        echo $1
    }

    f_over_range 0 5
    f_over_range 00 05

Notas:

  • Usar eval expõe risco de segurança de injeção de comando
  • O Linux imprimirá "00 \ n01 \ n02..etc", mas o OSX imprimirá "1 \ n2 \ n ... etc"
  • Usar seq ou estilo C para loops não corresponderá ao manuseio da expansão de chaves dos zeros à esquerda
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.