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úmero1
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 (los2
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 unaprintf
formato de estiloprintf
(como se utilizó anteriormente para relleno cero), o soluciones alternativas de Bash pura basadas eneval
(¡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
operl
.
- Utilice la utilidad
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
yksh
.
- por el contrario, el uso de variables funciona en
- ejemplo con prefijo y postfijo:
- 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
-
- valor de paso de incremento opcional:
- [raras] cadenas : solo letras inglesas únicas permitidas; por ejemplo,
- 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 ):
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 confind
, por ejemplo.
- Esto permite el uso de
-
echo {1..$to}
->''{1..<value-of-$to>}''
- inválido como expr de llave. enbash
: variables no soportadas; sin embargo, válido enksh
yzsh
. - (
fish
, por el contrario, expande cualquier secuencia{...}
; de manera similar,zsh
tiene la opciónBRACE_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í:
- Variables en el reemplazo de bash seq ({1..10}) 7 respuestas
#!/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