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.
REPLYes elnamepredeterminado para elreadcomando de shell integrado. - Tenga en cuenta que esto requiere que el shell use una variable predeterminada para colocar los datos de
read, quebashhace , pero esto no es estándar. Además, su comandoreadpuede 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 ,
2y3en 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*). Elechopuede 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'}")