¿Cómo puedo usar $ variable en una expansión de llave de shell de una secuencia?


33

Quiero usar $var inuna expansión de llave de shell con un rango, en bash. Simplemente poner {$var1..$var2}no funciona, así que fui "lateral" ...

Lo siguiente funciona, pero es un poco torpe.

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

¿Hay una forma más "normal"?

Respuestas:


26

Es posible que desee probar:

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

No estoy seguro de si esta es la mejor respuesta, pero ciertamente es una.


Durrh! :( No pude ver lo obvio ... Gracias :)
Peter.O

44
En bash, la expansión {} ocurre antes de la expansión $, por lo que no creo que haya otra forma de hacerlo que no sea usar eval o algún otro truco para causar dos pasadas a través de la expresión.
mattdm

66
Yo sólo "tengo" que! ... No hay ninguna expansión de llaves con {$one..$three}, porque no es una forma válida de llave de expansión, que en este caso espera enteros ... Sólo se convierte en una forma válida después de la expansión de $ var, que evalluego pasa por el mismo proceso para generar una expansión normal de la secuencia de llaves ... 1 2 3 QED;) ... Resumen: la simple presencia de un par de llaves no activa la expansión de llaves ... solo una forma válida lo activa .
Peter.O

@fred: Bienvenido :-)
asoundmove

Cuando se usa una variable como a={0..9}; echo $a;no hay extensión de llave . Usando eval, funciona. Así que creo que la explicación de @ mattdm es buena.
dr0i

20

Como usted ya se dio cuenta, {1..3}se expande a 1 2 3pero {foo..bar}o {$foo..$bar}no haga la expansión de llaves gatillo, y el último se amplió posteriormente para reemplazar $fooy $barpor sus valores.

Una falla en GNU (por ejemplo, Linux no incorporado) es el seqcomando

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

Otra posibilidad. si foo.no contiene caracteres especiales de shell, es

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

La solución más simple es usar zsh, donde rm foo.{$ext0..$extN}hace lo que quiere.


Gracias por las opciones ... Es bueno verlos todos agrupados ... Me quedaré con bash y eval ... eval es lo suficientemente simple ahora que sé lo que está sucediendo detrás de escena; así que ya no es un problema. No es necesario cambiar un buen caballo por el jinete
:)

1

Mientras que las otras respuestas tratan sobre el uso evaly seq, en bash, puede usar un forbucle de estilo C tradicional en un contexto aritmético. Las variables ext0y extNse expanden dentro de la ((..))causa de que el ciclo se ejecute para el rango 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

Si está buscando una manera óptima y evita múltiples rmcomandos, puede usar un marcador de posición temporal para almacenar los resultados del nombre de archivo y llamar rmde una sola 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

y ahora llame al rmcomando en la 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:

  • El uso de eval expone el riesgo de seguridad de la inyección de comandos
  • Linux imprimirá "00 \ n01 \ n02..etc", pero OSX imprimirá "1 \ n2 \ n ... etc."
  • Usar seq o estilo C para bucles no coincidirá con el manejo de la expansión de llaves de ceros iniciales
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.