¿Puedo usar una variable en una expansión Bash brace?

10

A continuación hay una especie de pseudocódigo para lo que estoy tratando de lograr:

#!/bin/bash

# I already have the variable below figured out (positive integer):
numlines=$([returns number of lines containing specific characters in a file])

# This is basically what I want to do with it:
for i in {1..$numlines}; do
    # the part below is already figured out as well:        
    do some other stuff
done

Puedo ejecutarlo bien desde la línea de comando insertando el número real en la secuencia `{1..n} '. Solo necesito saber si es posible incluir una variable aquí y cómo hacerlo.

  • He tratado exporting se
  • He intentado poner la variable en sí misma entre llaves dentro de la secuencia: {1..${numlines}}
  • He intentado ponerlo entre comillas dobles con la esperanza de que se expanda: {1.."$numlines"}
  • He intentado escapar de $:{1..\$numlines}

¿Necesito usar un set -[something]comando para que esta variable se expanda? Incluso he intentado algunas formas de uso eval... todo en vano.

Solo necesito saber si hay algo simple u oscuro que me estoy perdiendo o si esto es posible antes de perder más tiempo.

Podría reunir una forma muy, muy dura de hacerlo para que funcione según sea necesario, pero me gustaría evitarlo si es posible y aprender la forma correcta de hacerlo.

rubynorails
fuente

Respuestas:

11

Desafortunadamente, no hay forma de usar una variable en esa expansión (AFAIK), ya que la expansión variable ocurre después de la expansión de la llave.

Afortunadamente, hay una herramienta que hace el mismo trabajo.

for i in $(seq 1 $numlines); do
    # stuff
done

seqes de GNU coreutils; No tengo idea de cómo hacerlo en POSIX.

Tom Hunt
fuente
1
(Más 1). seqes bueno para los sistemas GNU y, si no recuerdo mal, el OSX más reciente. En otros sistemas BSD, uno puede usar jot en su lugar.
John1024
seqfunciona perfectamente. Muchas gracias por su pronta respuesta.
rubynorails
Esto no era parte de mi pregunta, pero ¿cuál es la sintaxis (si la hay) para hacer esto en orden inverso, como {16..1}? $(seq $numlines 1)no funcionó. Supongo que siempre puedo man seq, pero me pregunto si alguien sabía de la cabeza.
rubynorails
1
Acabo de descubrir cómo hacerlo en reversa desde este enlace -for i in $(seq $numlines -1 1)
rubynorails
seq ${numlines} -1 0
DopeGhoti
10

Por supuesto. Si desea un bucle for que incremente una variable entera, use la forma del forbucle que incremente una variable entera (o más generalmente realiza aritmética en la (s) variable (s) del bucle).

for ((i=1; i<=numlines; i++)); do  done

Esta construcción funciona en bash (y ksh93 y zsh), pero no en sh simple. En sh simple, use un bucle while y la [ … ]construcción test ( ).

i=1
while [ "$i" -le "$numlines" ]; do
  
  i=$((i+1))
done
Gilles 'SO- deja de ser malvado'
fuente
8

Si debe evitar seq, lo que, como señala Tom Hunt, parece ser la solución habitual para esto, entonces evaldefinitivamente puede hacerlo (sin embargo, no lo recomendaría):

eval 'for i in {1..'$numlines'}; do echo $i; done'

Puede permanecer en POSIX evitando la expansión {}, y simplemente hacer comparaciones de matemáticas y enteros en $numlines:

while [ ! "$numlines" -eq 0 ]; do
     echo "$numlines"
     : $((numlines-=1))
done

Fuera de POSIX, bashy kshy zshtambién tienen estilo C forbucles:

for((i=0; i<numlines; i++)); do echo $i; done
PSkocik
fuente
1
Realmente aprecio esta respuesta también. Si seqbien funcionó bien para mi escenario y parecía ser la solución más simple, es bueno saber que hay otras alternativas (incluso POSIX). Gracias por esto.
rubynorails
2
Realmente no hay razón para usar eval; si tiene expansión de llaves, tiene el bucle de estilo C.
chepner
@PSkocik: si pudiera elegir 2 respuestas, también elegiría esta. Cuando me encontré con el hecho de que necesitaba hacer esto a la inversa, su evalejemplo fue el más simple y me habría evitado tener que buscar una forma alternativa de hacerlo usando seq. El whilebucle es un poco voluminoso para mí. Me gusta mantener las cosas cortas y dulces, y nunca pude hacer forque funcione el bucle de estilo C dando iel valor de 0 o 1. Nunca regresó correctamente y siempre estaba un poco apagado. Estoy seguro de que podría modificarse para que funcione correctamente, pero estas son definitivamente soluciones útiles, no obstante.
rubynorails
El evalenfoque es problemático si hay algo no trivial dentro del cuerpo del bucle. Me imagino que no sería muy legible si necesitaras anidar dos de estos bucles.
kasperd