Comando Awk dentro de un bucle for

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

  • Cada carácter entre un par de comillas simples se trata como un carácter literal.
  • ¿Cuál es su caso de uso real? hacer un bucle sobre awk parece innecesario aquí (por ejemplo, en bash, podría usar una sustitución de parámetro echo "${var//,/$'\n'}")
  • En realidad, la variable contendrá urls de una forma zenity. Quiero usar estas URL por separado, así que tengo que obtener cada una de ellas de forma independiente.
  • Relevante, pero no es una buena solución en este caso: Cómo asignar valor en tiempo de ejecución en el comando AWK

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átil printf "$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 el name predeterminado para el read comando de shell integrado.
  • Tenga en cuenta que esto requiere que el shell use una variable predeterminada para colocar los datos de read, que bash hace , pero esto no es estándar. Además, su comando read 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

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

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 y 3 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 *). El echo 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 

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *