9. Acciones: Sentencias de Control

Las sentencias de control tales como if, while, y así sucesivamente controlan el flujo de la ejecución de los programas awk. La mayoría de las sentencias de control de awk son similares a sus sentencias análogas en Lenguaje C.

Todas las sentencias de control comienzan con las palabras claves tales como if y while, para distinguirlas de las expresiones simples.

Muchas sentencias de control contienen otras sentencias; por ejemplo, la sentencia if contiene otra sentencia la cual podría ser ejecutada o no. Las sentencias contenidas se llaman el cuerpo. Si quieres incluir más de una sentencia en el cuerpo, agrupalas en una única sentencia compuesta con llaves, separándose entre ellas con puntos y comas y saltos de línea.

La Sentencia if

La sentencia if–else es una sentencia para la toma de decisiones de awk. Presenta la siguiente forma:

if (condición) cuerpo–then [else cuerpo–else]

Aquí condición es una expresión que controla qué es lo que realizará el resto de la sentencia. Si la condición es verdad, entonces se ejecuta el cuerpo–then; sino se ejecuta el cuerpo–else (asumiendo que esté presente la clásula else). La parte else de la sentencia es opcional. La condición se considera falsa si su valor es 0 o una cadena nula, sino se considerará verdadera.

Aquí se presenta un ejemplo:

if (x % 2 == 0)

    print "x es par"

else

    print "x es impar"

En este ejemplo, si la expresión x%2 == 0 es cierta (es decir, el valor de x es divisible entre 2), entonces se ejecuta la primera sentencia print, sino se ejecuta la segunda sentencia print.

Si el else aparece en la misma línea que el cuerpo–then, y el cuerpo–then no es una sentencia compuesta (no aparece entre llaves), entonces un punto y coma debe separar el cuerpo–then del else. Para ilustrar esto veamos el siguiente ejemplo:

awk '{ if (x % 2 == 0) print "x is even"; else

        print "x is odd" }'

Si olvidas el ‘;’ awk no será capaz de realizar el parse de la sentencia, y obtendrás un error de sintáxis. De todas formas es una mala práctica escribir la sentencia else de esta forma, ya que puede llevar al lector a confusión.

La Sentencia while

En programación, un bucle significa una parte de programa que es (o al menos puede ser) ejecutada dos o más veces seguidas.

La sentencia while es la sentencia de bucle más simple de awk. Ejecuta repetidamente una sentencia mientras una condición sea cierta. Presenta la siguiente forma:

while (condición)

  cuerpo

Aquí cuerpo es una sentencia a la que llamamos el cuerpo del bucle, y condición es una expresión que controla cuantas veces se ejecuta dicho cuerpo.

La primera cosa que hace la sentencia while es comprobar la condición. Si condición es cierta, ejecuta la sentencia cuerpo. (Verdad, como es usual en awk, significa que el valor de la condición es distinto de cero y la cadena nula). Después de que el cuerpo se haya ejecutado, se chequea la condición de nuevo, y si sigue siendo cierta, el cuerpo se ejecuta de nuevo. Este proceso se repite hasta que la condición deja de ser cierta. Si la condición es falsa inicialmente, el cuerpo del bucle nunca es ejecutado.

Este ejemplo imprime los tres primeros campos de cada registro, uno por línea.

awk '{ i = 1

       while (i <= 3) {

           print $i

           i++

       }

}'

Aquí el cuerpo del bucle es una sentencia compuesta encerrada entre llaves, que contiene dos sentencias.

El bucle trabaja de la siguiente forma: primero, el valor de i es fijado a 1. Entonces, el while chequea si i es menor o igual a tres. Este es el caso cuando i es igual a uno, así que el campo iésimo es impreso. Entonces el comando i++ incrementa el valor de i y se repite el bucle. El bucle termina cuando i alcanza el valor de 4.

Como puedes comprobar, un carácter de nueva línea no es requerida entre la condición y el cuerpo; pero usándola hace que el programa sea más claro a menos que el cuerpo sea una sentencia compuesta o una muy simple. El carácter newline después de la llave de apertura que inicia la sentencia compuesta no es obligatoria, pero el programa será menos legible sino se pone.

La Sentencia do–while

El bucle do es una variación de la sentencia de bucle while. El bucle do ejecuta el cuerpo al menos una vez, después repite el cuerpo mientras la condición se siga evaluando a cierto. Presenta la siguiente forma:

do

  cuerpo

while (condición)

Incluso aunque condición sea falsa inicialmente, el cuerpo se ejecuta al menos una vez (y sólo una vez, a menos que el cuerpo de ejecución siga haciendo que la condición sea cierta). Contrasta esto con la correspondiente sentencia while:

while (condición)

  cuerpo

Esta sentencia no ejecuta el cuerpo ni una vez si la condición es falsa antes de empezar.

Aquí está un ejemplo de una sentencia do:

awk '{ i = 1

       do {

          print $0

          i++

       } while (i <= 10)

}'

imprime cada registro de entrada 10 veces. No es un ejemplo muy realista, ya que un while ordinario podría haber hecho lo mismo.

La Sentencia for

La sentencia for es más conveniente para contar las iteraciones de un bucle. La forma general de la sentencia for presenta la siguiente forma:

for (inicialización; condición; incremento)

  cuerpo

Esta sentencia comienza ejecutando inicialización. Después, mientras la condición sea cierta, ejecuta repetidamente el cuerpo y el incremento. Normalmente la inicialización fija el valor de la variable a 0 o 1, incremento añade 1 a dicho valor, y condición compara el nuevo valor contra el número de iteraciones deseado.

Aquí tienes un ejemplo de una sentencia for:

awk '{ for (i = 1; i <= 3; i++)

          print $i

}'

Esto imprime los tres primers campos de cada registros de entrada, un campo por línea.

En la sentencia for, el cuerpo está formado por sentencias, pero inicialización, condición e incremento son simples expresiones. No puedes fijar el valor de más de una variable en la parte inicialización a menos que uses una sentencia de asignación múltiple tal como x = y = z = 0, la cual sólo es posible si todos los valores iniciales son el mismo (pero puedes inicializar variables adicionales escribiendo sus asignaciones como sentencias separadas que precedan al bucle for).

Lo mismo se cumple para la parte incremento; para incrementar variables adicionales, debes escribir sentencias separadas al final del bucle. La expresión compuesta C, que usa el separador de C coma, sería útil en este contexto, pero no está soportada por awk.

En la mayoría de los casos, incremento es una expresión incremental, como en el ejemplo de arriba. Pero esto no es obligatorio; podría ser cualquier expresión. Por ejemplo, esta sentencia imprime todas las potencias de 2 entre 1 y 100:

for (i = 1; i <= 100; i *= 2)

  print i

Cualquiera de las tres expresiones en los paréntesis que siguen al for podría ser omitida si no fuese necesaria. Por lo que, ‘for (;x > 0;)’ es equivalente a ‘while (x > 0)’. Si la condición se omite, se trata como cierta, dando lugar a un bucle infinito efectivo.

En la mayoría de los casos, un bucle for es una abreviación de un bucle while, tal y como se muestra aquí:

inicialización

while (condición) {

  cuerpo

  incremento

}

La única excepción es cuando se utiliza la sentencia continue (Ver la sección La Sentencia continue) dentro del bucle; cambiar una sentencia for por una sentencia while de esta forma puede cambiar el efecto de la sentencia continue dentro del bucle.

Existe una versión alternativa del bucle for, para hacer una iteración para cada índice de un array:

for (i in array)

    hacer algo con array[i]

Ver la sección 10. Arrays en awk, para más información de esta versión del bucle for.

El Lenguaje awk tiene una sentencia for además de la sentencia while porque a menudo un bucle for es más fácil para escribirla y más natural para pensarla y entenderla. Contar el número de iteraciones es muy común en los bucles. Es más fácil pensar este conteo como parte del bucle en lugar de cómo algo a hacer dentro del bucle. La siguiente sección tiene ejemplos más complicados de bucles for.

La Sentencia break

La sentencia break se sale del bucle for, while o do–while más interno en el que está contenido. El siguiente ejemplo encuentra el divisor más pequeño de cualquier entero, y también identifica números primos:

awk '# encuentra el divisor más pequeño de num

     { num = $1

       for (div = 2; div*div <= num; div++)

         if (num % div == 0)

           break

       if (num % div == 0)

         printf "Smallest divisor of %d is %d\n", num, div

       else

         printf "%d is prime\n", num  }'

Cuando el resto es cero en la primera sentencia if, awk se sale inmediatamente del bucle for en el que está contenido. Esto significa que awk procede inmediatamente a la sentencia que sigue al bucle y continúa el procesamiento. (Esto es muy distinto de la sentencia exit (ver la sección La Sentencia exit) la cual detiene el programa awk completo.)

Aquí está otro programa equivalente al anterior. Ilusta como la condición de un for o while podría simplemente ser substituida con un break dentro de un if:

awk '# find smallest divisor of num

     { num = $1

       for (div = 2; ; div++) {

         if (num % div == 0) {

           printf "Smallest divisor of %d is %d\n", num, div

           break

         }

         if (div*div > num) {

           printf "%d is prime\n", num

           break

         }

       }

}'

La Sentencia continue

La sentencia continue, al igual que break, se usa solamente dentro de un bucle for, while o do–while. Lo que hace es saltarse todas las sentencias que falten por ejecutarse dentro del cuerpo del bucle y volver a chequear automáticamente la condición del bucle. Contrasta con la sentencia break, en que ésta produce directamente un salto fuera del bucle. Aquí se presenta un ejemplo:

# print names that don't contain the string "ignore"

# first, save the text of each line

{ names[NR] = $0 }

# print what we're interested in

END {

   for (x in names) {

       if (names[x] ~ /ignore/)

           continue

       print names[x]

   }

}

Si uno de los registros de entrada contiene la cadena ‘ignore’, este ejemplo se salta la sentencia print para ese registro, y vuelve a la primera sentencia del bucle.

Este no es un ejemplo práctico de continue, ya que está sería una forma más sencilla de escribir el bucle:

for (x in names)

  if (names[x] !~ /ignore/)

    print names[x]

La sentencia continue en un bucle for hace que awk se salte el resto del cuerpo del bucle, y resume la ejecución con la expresión incremento de la sentencia for. El siguiente programa ilustra esto de hecho:

awk 'BEGIN {

     for (x = 0; x <= 20; x++) {

         if (x == 5)

             continue

         printf ("%d ", x)

     }

     print ""

}'

Este programa imprime todos los números de 0 a 20, excepto el 5 para el cual se salta el print. Ya que el incremento no es saltado, x no permanece estancado en 5. Contrasta el bucle for de arriba con el bucle while:

awk 'BEGIN {

     x = 0

     while (x <= 20) {

         if (x == 5)

             continue

         printf ("%d ", x)

         x++

     }

     print ""

}'

Este programa se convierte en un bucle infinito una vez que x alcanza el valor de 5.

La Sentencia next

La sentencia next fuerza a awk que detenga inmediatamente el procesamiento del registro actual y vaya a leer el siguiente registro. Esto significa que no se ejecutará ninguna regla más para el registro en curso. El resto de las acciones de la regla actual no se ejecutarán.

Contrasta esto con el efecto de la función getline (Ver la sección Entrada explícita con getline). Eso también hace que awk lea el siguiente registro inmediatamento, pero no se altera el control de flujo del programa. De forma que el resto de la acción actual se ejecuta con un nuevo registro de entrada.

En el nivel más alto, la ejecución de un programa awk es un bucle que lee un registro de entrada y entonces lo chequea contra el patrón de cada regla. Si piensas en este bucle como una sentencia for cuyo cuerpo contiene reglas, entonces la sentencia next es análoga a una sentencia continue: se salta el resto del cuerpo de este bucle implícito, y ejecuta el imcremento (lo que provoca la lecuta de otro registro).

Por ejemplo, si tu programa awk trabaja sólo sobre registros con cuatro campos, y no quieres que procese una entrada incorrecta, podrías usar esta regla cerca del principio del programa:

NF != 4 {

  printf("line %d skipped: doesn't have 4 fields", FNR) > "/dev/stderr"

  next

}

de forma que las reglas siguientes no verán el registro incorrecto. El mensaje de error se redirecciona al streem de salida de error estándar, como debiera ser para los mensajes de error. Ver la sección Streams de Entrada/Salida Estándard.

La sentencia next no está permitida en una regla BEGIN o END.

La Sentencia exit

La sentencia exit hace que awk detenga la ejecución de la regla actual inmediatamente y detenga el procesamiento de la entrada, cualquier registro que falte es ignorado.

Si una sentencia exit se ejecuta en una regla BEGIN el programa detiene el procesamiento de todo inmediatamente. No se leería ningún registro de entrada. Sin embargo, si existe una regla END, ésta es ejecutada (Ver la sección Los Patrones Especiales BEGIN y END).

Si se usa exit como parte de una regla END, hace que el programa se detenga inmediatamente.

Una sentencia exit como parte de una regla ordinaria (esto es, no forme parte de una regla BEGIN y END) detiene la ejecución de cualquier regla automática posterior, pero la regla END sí es ejecutada si existe. Si no quieres que la regla END realice su trabajo en estos casos, puedes fijar una variable a distinto de cero antes de la sentencia exit, y chequear dicha variable en la regla END.

Si se le suministra un argumento a exit, su valor se usa como el código de estado de salida para el proceso awk. Si no se le suministra argumento, exit devuelve el estado 0 (éxito). Por ejemplo, digamos que has descubierto una condición de error que no sabes como manejar realmente. Convencionalmente, el programa te reporta esto saliéndose con un estado distinto de cero. Tu programa awk puede hacer esto usando una sentencia exit con un argumento distinto de cero. Aquí tienes un ejemplo de esto:

BEGIN {

       if (("date" ¦ getline date_now) < 0) {

         print "Can't get system date" > "/dev/stderr"

         exit 4

       }

}

   
Índice
Manual