expansions bash loops variables for-loop variable-expansion

bash - expansions - Expansión de la abrazadera con variable?



bash expansions (6)

Soluciones para no poder usar variables en una expresión de secuencia de llaves:

  • Si la intención es simplemente iterar sobre números en un rango , como en el caso del OP, la mejor opción es no utilizar la expansión de llaves , sino usar el bucle de estilo C de bash, ver la respuesta del usuario000001 .

    • Si los números específicos no son importantes y simplemente necesita ejecutar un cuerpo de bucle un número específico de veces, la respuesta de Cole Tierney es una opción.
  • Si se desea el uso de la expansión de la abrazadera, no obstante:

    • Si NO necesita los números en la lista para tener un prefijo o postfijo , use la utilidad seq con una sustitución de comando sin comillas ( advertencia pequeña : seq NO es una utilidad POSIX, pero está ampliamente disponible); p.ej

      • echo $(seq 3) -> 1 2 3 ; comenzar el número 1 implicado
        • echo $(seq -f ''%02.f'' 3) -> 01 02 03 - relleno cero
      • echo $(seq 2 4) -> 2 3 4 ; números explícitos de inicio y final
      • echo $(seq 1 2 5) -> 1 3 5 ; incremento personalizado (los 2 en el medio )
    • Si necesita los números en la lista para tener un prefijo o un sufijo , tiene varias opciones:

      • Utilice la utilidad seq con su opción -f para proporcionar una printf formato de estilo printf (como se utilizó anteriormente para relleno cero), o soluciones alternativas de Bash pura basadas en eval (¡cuidado adicional necesario!) O construir una matriz en un bucle, todas que se detallan en esta respuesta .
      • También podría considerar implementar la funcionalidad genéricamente, como escribir una función de shell personalizada o una secuencia de comandos personalizada con utilidades como awk o perl .

Ejemplo de uso seguro de eval con variables que impulsan una expresión de secuencia de llaves:

Las variables se validan de antemano, para asegurarse de que contengan enteros decimales.

from=1 to=3 # sample values # Ensure that $from and $to are decimal numbers and abort, if they are not. (( 10#$from + 10#$to || 1 )) 2>/dev/null || { echo "Need decimal integers" >&2; exit 1; } eval echo "A{$from..$to}" # -> ''A1 A2 A3''

Descripción general de la expansión de la abrazadera

El objetivo principal de la expansión del paréntesis es expandirse a una lista de tokens con cada token con un prefijo y / o postfijo opcionales ; Las expansiones de corsé deben ser sin comillas y vienen en 2 sabores :

  • una serie fija (lista) de cadenas separadas por comas - variables admitidas
    • especifica y se expande a un número fijo de tokens (2 o más); p.ej:
    • echo A{b,c,d} -> Ab Ac Ad , es decir, 3 tokens, como lo implica el número de args.
    • echo {/,$HOME/}Library , por ejemplo, -> /Library /User/jdoe/Library
    • Las referencias de variables, e incluso los globs, son compatibles, pero tenga en cuenta que se expanden después de la expansión del paréntesis, en su resultado , en el transcurso de la evaluación normal.
  • una expresión de secuencia (rango) con .. , típicamente numérico - variables NO compatibles

    • se expande a un número variable de tokens , impulsado por los puntos de inicio y fin de los literales (por razones históricas , NO se admite el uso de variables - vea los comentarios en la respuesta de user000001 ):
      • [raras] cadenas : solo letras inglesas únicas permitidas; por ejemplo, {a..c}
      • números : enteros decimales solamente ; por ejemplo, {1..10} , {10..1} , {-1..2}
        • ejemplo con prefijo y postfijo: A{1..3}# -> A1# A2# A3#
        • ejemplo roto con variables : {$from..$to} # !! FAILS {$from..$to} # !! FAILS - $from y $to se interpretan como literales y, por lo tanto, no se reconocen como una sola letra o como un entero decimal: no se realiza ninguna expansión de llaves (ver a continuación).
          • por el contrario, el uso de variables funciona en zsh y ksh .
      • Bash 4+ agrega dos características:
        • valor de paso de incremento opcional:
          • echo A{1..5..2} -> A1 A3 A5 - números incrementados por 2
        • capacidad de cero almohadilla :
          • echo A{001..003} -> A001 A002 A003
  • Una expresión de llave inválida no se expande (se trata como una cadena normal sin comillas, con { y } tratados como literales ):

    • echo {} -> ''{}'' - inválido como expr .: al menos 2 , necesitan tokens separados
      • Esto permite el uso de {} sin comillas con find , por ejemplo.
    • echo {1..$to} -> ''{1..<value-of-$to>}'' - inválido como expr de llave. en bash : variables no soportadas; sin embargo, válido en ksh y zsh .
    • ( fish , por el contrario, expande cualquier secuencia {...} ; de manera similar, zsh tiene la opción BRACE_CCL (OFF por defecto) para expandir caracteres individuales dentro de {..} , que efectivamente causa la expansión de cualquier secuencia no vacía {...} . )

Esta pregunta ya tiene una respuesta aquí:

#!/bin/sh for i in {1..5} do echo "Welcome" done

Funcionaría, muestra Bienvenido 5 veces.

#!/bin/sh howmany=`grep -c $1 /root/file` for i in {1..$howmany} do echo "Welcome" done

No funciona! howmany sería igual a 5 ya que eso es lo que mostraría la salida de grep -c . $ 1 es el parámetro 1, que es específico al ejecutar el script.

¿Algunas ideas?


El problema es que la "expansión de llave" se realiza antes de la "expansión variable"

for i in $(seq 1 $howmany)

funciona como dijo @damienfrancois, o, si lo desea:

for i in $(eval echo ''{$start..10}'')

probablemente sí, pero no lo use para la cordura de todos.


La expansión del corsé se evalúa antes de que las variables se expandan. Necesitas un estilo c para loop en su lugar:

for ((i=1;i<=howmany;i++)) do echo "Welcome" done


También podría usar un ciclo while:

while ((howmany--)); do echo "Welcome" done


También podríamos usar eval en este caso:

howmany=`grep -c $1 /root/file` for i in $(eval echo {1..$howmany}); do echo "Welcome" done


crea una secuencia para controlar tu bucle

for i in $(seq 1 $howmany); do echo "Welcome"; done