Intento, sin éxito, usar un comando awk
dentro de un for
bucle.
Tengo una variable que contiene una serie de cadenas que quiero cortar con awk
para obtener los datos.
Sé cómo hacerlo, pero lo que realmente quiero es cortar los datos sucesivamente.
Así que tengo esta variable:
var="data1,data2,data3"
Y aquí donde estoy ahora:
for ((i=1; i<=3; i++)) do echo $(awk -F, "{print $1}" <<< $var) done
Intento reemplazar el $1
por el bucle $i
pero sin éxito.
Comentarios
Responder
Puede lograr lo que «desea tratando de hacerlo usando comillas dobles en el script awk para inyectar la variable de shell en él. Aún desea mantener un $
literal en él, lo que puede hacer escapándolo con una barra invertida :
echo $(awk -F, "{print \$$i}" <<<$var)
Esto ampliará $i
a 1
, 2
y 3
en cada una de las iteraciones, por lo tanto, awk verá $1
, $2
y $3
lo que hará que se expanda cada uno de los campos.
Otra posibilidad es inyectar la variable de shell como una awk con el indicador -v
:
echo $(awk -F, -v i="$i" "{print $i}" <<<$var)
Eso asigna la variable awk i al contenido de la variable de shell con el mismo nombre. Las variables en awk no usan un $
, que se usa para campos, por lo que $i
es suficiente para referirse al i -th campo si i es una variable en awk.
Asignar una variable awk con -v
es generalmente más seguro enfoque, particularmente cuando puede contener secuencias arbitrarias de caracteres, en ese caso hay menos riesgo de que el contenido se ejecute como código awk en contra de sus intenciones. Pero dado que en su caso la variable contiene un solo entero, eso es menos preocupante.
Otra opción más es usar un bucle for
en awk. . Consulte la documentación de awk (o busque en este sitio) para obtener más detalles sobre cómo hacerlo.
Comentarios
- … o establezca el registro de entrada separador apropiadamente
awk -v RS='[,\n]' 1 <<< "$var"
(o tal vez más portátilprintf "$var" | awk -v RS=, 1
) - @steeldriver Ese script awk imprimirá los tres campos en una vez. Lo cual está bien dado que el OP está haciendo solo eso … Terminé enfocando la respuesta en extraer un solo campo en el supuesto de que estaban interesados en ejecutar otros comandos en el ciclo de shell (que podría han sido la motivación para usar uno en primer lugar …)
- Entendido, que ‘ es la razón por la que comenté sobre el OP para aclarar lo que realmente quiero hacer;)
- La primera es una inyección (y una preocupación de seguridad), la segunda o ne (usando
-v
) no es una inyección. - Gracias, ¡tu respuesta resolvió mi problema! La primera vez que publico una pregunta aquí después de años de leer publicaciones. No decepcionado. ¡Qué gran comunidad!
Respuesta
Usar awk
parece excesivo en esta circunstancia, ¿qué tal un tr
y un ciclo while:
tr , "\n" <<<"$var" | while read; do echo $REPLY done
Salida:
data1 data2 data3
Comentarios
- Genial.
REPLY
es elname
predeterminado para elread
comando de shell integrado. - Tenga en cuenta que esto requiere que el shell use una variable predeterminada para colocar los datos de
read
, quebash
hace , pero esto no es estándar. Además, su comandoread
puede modificar los datos si contiene barras invertidas y puede eliminar los espacios en blanco que flanquean los valores. También leería valores con nuevas líneas incrustadas como valores múltiples. Además, necesita"$REPLY"
para evitar que el shell divida el valor y realice la expansión del nombre de archivo en él.
Respuesta
awk puede aceptar tanto j
(como variable) y $j
(como índice de campo):
for i in 1 2 3; do echo "$var" | awk -v j=$i -F , "{print $j}"; done
$i
en el ejemplo» confuso «awk
cuál usar (shell o su propia variable, teniendo prioridad) ya que ambos se denominan con el prefijo $
.
note
sh shell, que es estándar para secuencias de comandos «portátiles», no admite:
(( i=1; i<=3; i++; ))
y <<< $var
construcciones
También podría considerar usar el comando seq
en for
bucle para un control más preciso en la generación de secuencia numérica, si está disponible.
Comentarios
- Su bucle solo funcionaría si tuviera tres archivos llamados ,
2
y3
en el directorio actual. Además, usa la expansión de variables sin comillas en el shell, lo que puede tener consecuencias no deseadas si los datos contienen patrones de nombre de archivo (como*
). Elecho
puede además modificar los datos si contienen barras invertidas.
Respuesta
#!/bin/sh var="data1,data2,data3" unset data while [ "$var" != "$data" ]; do data=${var%%,*} # delete first comma and the bit after it var=${var#*,} # delete bit up to first comma (and the comma) printf "data = "%s"\n" "$data" done
Aquí, usamos sustituciones de variables para obtener cada campo de datos sucesivos delimitados por comas a partir del valor de la variable var
. La primera asignación a data
en el ciclo eliminará todo de $var
después de la primera coma. Luego, la variable var
se modifica para eliminar el primer bit hasta la primera coma.
Esto continúa hasta "$var" = "$data"
lo que significa que no se puede hacer nada más con la cadena.
Esta forma de hacerlo nos permitiría manejar cadenas de datos separadas por comas que contienen nuevas líneas incrustadas:
var="line1 line2,data2,last bit goes here"
Con los valores anteriores en var
, la secuencia de comandos anterior generaría
data = "line1 line2" data = "data2" data = "last bit goes here"
Sin importar las nuevas líneas integradas; Rara vez tiene que recorrer las invocaciones de awk
.
Tenga en cuenta que awk
está perfectamente feliz de leer su cadena como un conjunto de campos delimitados por comas, y que es capaz de recorrer estos:
printf "%s\n" "$var" | awk -F "," "{ for (i=1; i<=NF; i++) print $i }"
Con var="data1,data2,data3"
, esto se imprimiría
data1 data2 data3
Otra solución de shell que hace uso de IFS
para dividir el valor $var
en bits y al mismo tiempo usar set -f
para deshabilitar la expansión del nombre de archivo:
set -f oldIFS=$IFS; IFS="," set -- $var IFS=$oldIFS; unset oldIFS set +f for data do printf "data = "%s"\n" "$data" done
echo "${var//,/$'\n'}"
)