learnxinyminutes-docs/es/awk.md

362 lines
12 KiB
Markdown
Raw Permalink Normal View History

2017-07-15 07:27:55 +00:00
---
filename: learnawk-es.awk
contributors:
- ["Marshall Mason", "http://github.com/marshallmason"]
translators:
- ["Hugo Guillén-Ramírez", "http://github.com/HugoGuillen"]
---
2017-07-15 07:40:58 +00:00
AWK es una herramienta estándar en cada sistema UNIX compatible con POSIX.
Es como un Perl restringido, perfecto para tareas de procesamiento de texto y
otras necesidades de scripting. Tiene una sintaxis similar a C, pero sin
puntos y comas, manejo manual de memoria y tipado estático. Puedes llamarlo
2017-07-15 07:40:58 +00:00
desde un script de shell o usarlo como un lenguaje stand-alone para scripting.
¿Por qué elegir AWK sobre Perl? Principalmente, porque AWK es parte de UNIX.
Siempre puedes contar con él, mientras que el futuro de Perl está en duda. AWK
es más fácil de leer que Perl. Para scripts sencillos de procesamiento de texto,
particularmente si es para leer archivos línea a línea y dividir por
delimitadores, probablemente AWK es la herramienta correcta para el trabajo.
2017-07-15 07:27:55 +00:00
```awk
#!/usr/bin/awk -f
2017-07-15 08:16:11 +00:00
# Los comentarios tienen este aspecto.
# Los programas AWK son una colección de patrones y acciones. El patrón más
# importante es BEGIN. Las acciones van en bloques delimitados por llaves.
2017-07-15 07:27:55 +00:00
BEGIN {
2017-07-15 08:16:11 +00:00
# BEGIN correrá al inicio del programa. Es donde pones todo el código
# preliminar antes de procesar los archivos de texto. Si no tienes archivos
# de texto, piensa en BEGIN como el punto de entrada principal del script.
2017-07-15 07:27:55 +00:00
2017-07-15 08:16:11 +00:00
# Las variables son globales. Asígnalas o úsalas sin declararlas.
2017-07-15 07:27:55 +00:00
count = 0
2017-07-15 08:16:11 +00:00
# Los operadores son justo como en C (y amigos).
2017-07-15 07:27:55 +00:00
a = count + 1
b = count - 1
c = count * 1
d = count / 1
2017-07-15 08:16:11 +00:00
e = count % 1 # módulo
f = count ^ 1 # exponenciación
2017-07-15 07:27:55 +00:00
a += 1
b -= 1
c *= 1
d /= 1
e %= 1
f ^= 1
2017-07-15 08:16:11 +00:00
# Incremento y decremento en uno
2017-07-15 07:27:55 +00:00
a++
b--
2017-07-15 08:16:11 +00:00
# Como un operador prefijo, regresa el valor modificado
2017-07-15 07:27:55 +00:00
++a
--b
2017-07-15 08:16:11 +00:00
# Nota que no hay puntación para terminar las instrucciones
2017-07-15 07:27:55 +00:00
2017-07-15 08:16:11 +00:00
# Instrucciones de control
2017-07-15 07:27:55 +00:00
if (count == 0)
2017-07-15 08:16:11 +00:00
print "Iniciando count en 0"
2017-07-15 07:27:55 +00:00
else
2017-07-15 08:16:11 +00:00
print "Eh?"
2017-07-15 07:27:55 +00:00
2017-07-15 08:16:11 +00:00
# O puedes usar el operador ternario
print (count == 0) ? "Iniciando count en 0" : "Eh?"
2017-07-15 07:27:55 +00:00
2017-07-15 08:16:11 +00:00
# Bloques formados por múltiples líneas usan llaves
2017-07-15 07:27:55 +00:00
while (a < 10) {
print "La concatenación de strings se hace " " con series "
print " de" " strings separados por espacios"
2017-07-15 07:27:55 +00:00
print a
a++
}
for (i = 0; i < 10; i++)
2017-07-15 08:16:11 +00:00
print "El viejo confiable ciclo for"
2017-07-15 07:27:55 +00:00
2017-07-15 09:02:03 +00:00
# Los operaciones de comparación son estándar...
a < b # Menor que
a <= b # Menor o igual que
a != b # No igual
a == b # Igual
a > b # Mayor que
a >= b # Mayor o igual que
# ...así como los operadores lógicos
2017-07-15 07:27:55 +00:00
a && b # AND
a || b # OR
2017-07-15 09:02:03 +00:00
# Además están las expresiones regulares
2017-07-15 07:27:55 +00:00
if ("foo" ~ "^fo+$")
print "Fooey!"
if ("boo" !~ "^fo+$")
print "Boo!"
# Arrays
arr[0] = "foo"
arr[1] = "bar"
2017-07-15 09:02:03 +00:00
# Desafortunadamente no hay otra manera de inicializar un array.
# Tienes que inicializar cada posición del array.
2017-07-15 07:27:55 +00:00
2017-07-15 09:02:03 +00:00
# También hay arrays asociativos
2017-07-15 07:27:55 +00:00
assoc["foo"] = "bar"
assoc["bar"] = "baz"
2017-07-15 09:02:03 +00:00
# Y arrays multidimensionales con limitaciones que no mencionaré aquí
2017-07-15 07:27:55 +00:00
multidim[0,0] = "foo"
multidim[0,1] = "bar"
multidim[1,0] = "baz"
multidim[1,1] = "boo"
2017-07-15 09:02:03 +00:00
# Puedes probar pertenencia a un array
2017-07-15 07:27:55 +00:00
if ("foo" in assoc)
print "Fooey!"
2017-07-15 09:02:03 +00:00
# También puedes usar el operador 'in' para iterar las claves de un array
2017-07-15 07:27:55 +00:00
for (key in assoc)
print assoc[key]
2017-07-15 09:02:03 +00:00
# La terminal es un array especial llamado ARGV
2017-07-15 07:27:55 +00:00
for (argnum in ARGV)
print ARGV[argnum]
2017-07-15 09:02:03 +00:00
# Puedes eliminar elementos de un array.
# Esto es útil para prevenir que AWK suponga que algunos argumentos
# son archivos por procesar.
2017-07-15 07:27:55 +00:00
delete ARGV[1]
2017-07-15 09:02:03 +00:00
# El número de argumentos de la terminal está en la variable ARGC
2017-07-15 07:27:55 +00:00
print ARGC
2017-07-15 09:02:03 +00:00
# AWK tiene tres categorías de funciones incluidas.
# Demostraré esas funciones posteriormente.
2017-07-15 07:27:55 +00:00
return_value = arithmetic_functions(a, b, c)
string_functions()
io_functions()
}
2017-07-15 09:02:03 +00:00
# Así se define una función
2017-07-15 07:27:55 +00:00
function arithmetic_functions(a, b, c, localvar) {
2017-07-15 09:02:03 +00:00
# Probablemente la parte más molesta de AWK es que no hay variables locales
# Todo es global. No es problema en scripts pequeños, pero sí para
# scripts más grandes.
2017-07-15 07:27:55 +00:00
# Hay un work-around (mmm... hack). Los argumentos de las funciones son
2017-07-15 09:02:03 +00:00
# locales para la función, y AWK permite definir más argumentos de función
# de los que necesita, por lo que define las variables locales en la
2017-07-15 09:02:03 +00:00
# declaración como en la función de arriba. Como convención, agrega
# espacios en blanco para distinguir los parámetros de la función de las
# variables locales. En este ejemplo, a, b y c son parámetros y localvar es
# una variable local.
2017-07-15 07:27:55 +00:00
2017-07-15 09:02:03 +00:00
# Ahora, a demostrar las funciones aritméticas
2017-07-15 07:27:55 +00:00
2017-07-15 09:02:03 +00:00
# La mayoría de las implementaciones de AWK tienen funciones
# trigonométricas estándar
2017-07-15 07:27:55 +00:00
localvar = sin(a)
localvar = cos(a)
2018-07-28 18:48:25 +00:00
localvar = atan2(b, a) # arcotangente de b / a
2017-07-15 07:27:55 +00:00
2017-07-15 09:02:03 +00:00
# Y cosas logarítmicas
2017-07-15 07:27:55 +00:00
localvar = exp(a)
localvar = log(a)
2017-07-15 09:02:03 +00:00
# Raíz cuadrada
2017-07-15 07:27:55 +00:00
localvar = sqrt(a)
2017-07-15 09:02:03 +00:00
# Trucar un flotante a entero
2017-07-15 07:27:55 +00:00
localvar = int(5.34) # localvar => 5
2017-07-15 09:02:03 +00:00
# Números aleatorios
srand() # La semilla es el argumento. Por defecto usa el tiempo del sistema
localvar = rand() # Número aleatorio entre 0 y 1.
2017-07-15 07:27:55 +00:00
2017-07-15 09:02:03 +00:00
# Y aquí se regresa el valor
2017-07-15 07:27:55 +00:00
return localvar
}
function string_functions( localvar, arr) {
2017-07-15 18:33:24 +00:00
# AWK tiene algunas funciones para procesamiento de strings,
# y muchas dependen fuertemente en expresiones regulares.
2017-07-15 07:27:55 +00:00
2017-07-15 18:33:24 +00:00
# Buscar y remplazar, primer instancia (sub) o todas las instancias (gsub)
# Ambas regresan el número de matches remplazados.
2017-07-15 07:27:55 +00:00
localvar = "fooooobar"
sub("fo+", "Meet me at the ", localvar) # localvar => "Meet me at the bar"
gsub("e", ".", localvar) # localvar => "M..t m. at th. bar"
2017-07-15 07:27:55 +00:00
2017-07-15 18:50:55 +00:00
# Buscar una cadena que haga match con una expresión regular
# index() hace lo mismo, pero no permite expresiones regulares
match(localvar, "t") # => 4, dado que 't' es el cuarto caracter
2017-07-15 07:27:55 +00:00
2017-07-15 18:50:55 +00:00
# Separar con base en un delimitador
2017-07-15 07:27:55 +00:00
split("foo-bar-baz", arr, "-") # a => ["foo", "bar", "baz"]
2017-07-15 18:50:55 +00:00
# Otras funciones útiles
2017-07-15 07:27:55 +00:00
sprintf("%s %d %d %d", "Testing", 1, 2, 3) # => "Testing 1 2 3"
substr("foobar", 2, 3) # => "oob"
substr("foobar", 4) # => "bar"
length("foo") # => 3
tolower("FOO") # => "foo"
toupper("foo") # => "FOO"
}
function io_functions( localvar) {
2017-07-15 18:50:55 +00:00
# Ya has visto print
2017-07-15 07:27:55 +00:00
print "Hello world"
2017-07-15 18:50:55 +00:00
# También hay printf
2017-07-15 07:27:55 +00:00
printf("%s %d %d %d\n", "Testing", 1, 2, 3)
# AWK no tiene handles de archivos en sí mismo. Automáticamente abrirá un
# handle de archivo cuando use algo que necesite uno. El string que usaste
# para esto puede ser tratada como un handle de archivo para propósitos
# de I/O. Esto lo hace similar al scripting de shell:
2017-07-15 07:27:55 +00:00
print "foobar" >"/tmp/foobar.txt"
2017-07-15 18:50:55 +00:00
# Ahora el string "/tmp/foobar.txt" es un handle. Puedes cerrarlo:
2017-07-15 07:27:55 +00:00
close("/tmp/foobar.txt")
2017-07-15 18:50:55 +00:00
# Aquí está como correr algo en el shell
system("echo foobar") # => muestra foobar
2017-07-15 07:27:55 +00:00
2017-07-15 18:50:55 +00:00
# Lee una línea de la entrada estándar (stdin) y lo guarda en localvar
2017-07-15 07:27:55 +00:00
getline localvar
2017-07-15 18:50:55 +00:00
# Lee una línea desde un pipe
2017-07-15 07:27:55 +00:00
"echo foobar" | getline localvar # localvar => "foobar"
close("echo foobar")
2017-07-15 18:50:55 +00:00
# Lee una línea desde un archivo y la guarda en localvar
2017-07-15 07:27:55 +00:00
getline localvar <"/tmp/foobar.txt"
close("/tmp/foobar.txt")
}
# Como dije al inicio, los programas en AWK son una colección de patrones y
2017-07-15 18:50:55 +00:00
# acciones. Ya conociste el patrón BEGIN. otros patrones sólo se usan si estás
# procesando líneas desde archivos o stdin.
# Cuando pasas argumentos a AWK, son tratados como nombres de archivos a
# procesar. Los va a procesar todos, en orden. Imagínalos como un ciclo for
2017-07-15 18:50:55 +00:00
# implícito, iterando sobre las líneas de estos archivos. Estos patrones y
# acciones son como instrucciones switch dentro del ciclo.
2017-07-15 07:27:55 +00:00
/^fo+bar$/ {
2017-07-15 18:50:55 +00:00
# Esta acción se ejecutará por cada línea que haga match con la expresión
# regular /^fo+bar$/, y será saltada por cualquier línea que no haga match.
# Vamos a sólo mostrar la línea:
2017-07-15 07:27:55 +00:00
print
2017-07-15 18:50:55 +00:00
# ¡Wow, sin argumento! Eso es porque print tiene uno por defecto: $0.
# $0 es el nombre de la línea actual que se está procesando.
# Se crea automáticamente para ti.
2017-07-15 07:27:55 +00:00
# Probablemente puedas adivinar que hay otras variables $. Cada línea es
2017-07-15 18:50:55 +00:00
# separada implícitamente antes de que se llame cada acción, justo como lo
# hace shell. Y, como shell, cada campo puede ser accesado con $.
2017-07-15 07:27:55 +00:00
2017-07-15 18:50:55 +00:00
# Esto mostrará el segundo y cuarto campos de la línea
2017-07-15 07:27:55 +00:00
print $2, $4
2017-07-15 18:50:55 +00:00
# AWK automáticamente define muchas otras variables que te ayudan a
# inspeccionar y procesar cada línea. La más importante es NF
2017-07-15 07:27:55 +00:00
2017-07-15 18:50:55 +00:00
# Imprime el número de campos de esta línea
2017-07-15 07:27:55 +00:00
print NF
2017-07-15 18:50:55 +00:00
# Imprime el último campo de esta línea
2017-07-15 07:27:55 +00:00
print $NF
}
2017-07-15 18:50:55 +00:00
# Cada patrón es realmente un prueba de verdadero/falso. La expresión regular
# en el último patrón también es una prueba verdadero/falso, pero parte de eso
# estaba oculto. Si no le das un string a la prueba, supondrá $0, la línea que
# se está procesando. La versión completa de esto es:
2017-07-15 07:27:55 +00:00
$0 ~ /^fo+bar$/ {
2017-07-15 18:50:55 +00:00
print "Equivalente al último patrón"
2017-07-15 07:27:55 +00:00
}
a > 0 {
2017-07-15 18:50:55 +00:00
# Esto se ejecutará una vez por línea, mientras a sea positivo
2017-07-15 07:27:55 +00:00
}
2017-07-15 19:08:09 +00:00
# Y ya te das una idea. Procesar archivos de texto, leyendo una línea a la vez,
# y haciendo algo con ella, particularmente separando en un deliminator, es tan
# común en UNIX que AWK es un lenguaje de scripting que hace todo eso por ti
# sin que tengas que pedirlo. Basta con escribir los patrones y acciones
2017-07-15 19:08:09 +00:00
# basados en lo que esperas de la entrada y lo quieras quieras hacer con ella.
# Aquí está un ejemplo de un script simple, para lo que AWK es perfecto.
# El script lee un nombre de stdin y muestra el promedio de edad para todos los
# que tengan ese nombre. Digamos que como argumento pasamos el nombre de un
# archivo con este contenido:
2017-07-15 07:27:55 +00:00
#
# Bob Jones 32
# Jane Doe 22
# Steve Stevens 83
# Bob Smith 29
# Bob Barker 72
#
2017-07-15 19:08:09 +00:00
# Éste es el script:
2017-07-15 07:27:55 +00:00
BEGIN {
2017-07-15 19:08:09 +00:00
# Primero, pedir al usuario el nombre
print "¿Para qué nombre quieres el promedio de edad?"
2017-07-15 07:27:55 +00:00
2017-07-15 19:08:09 +00:00
# Recuperar una línea de stdin, no de archivos en la línea de comandos
2017-07-15 07:27:55 +00:00
getline name <"/dev/stdin"
}
2017-07-15 19:08:09 +00:00
# Ahora, hacer match con cada línea cuyo primer campo es el nombre dado
2017-07-15 07:27:55 +00:00
$1 == name {
2017-07-15 19:08:09 +00:00
# Aquí dentro tenemos acceso a variables útiles precargadas:
# $0 es toda la línea
# $3 es el tercer campo, la edad, que es lo que nos interesa
# NF es el número de campos, que debe ser 3
# NR es el número de registros (líneas) vistos hasta ahora
# FILENAME es el nombre del archivo que está siendo procesado
# FS es el campo separador, " " en este caso
# Y muchas más que puedes conocer ejecutando 'man awk' en la terminal.
# Llevar el registro de la suma y cuantas líneas han hecho match.
2017-07-15 07:27:55 +00:00
sum += $3
nlines++
}
# Otro patrón especial es END. Va a ejecutarse después de procesar todos los
2017-07-15 19:08:09 +00:00
# archivos de texto. A diferencia de BEGIN, sólo se ejecuta si le das dado una
# entrada a procesar. Se ejecutará después de que todos los archivos hayan sido
# leídos y procesados según las reglas y acciones que programaste. El propósito
# es usualmente para mostrar un reporte final, o hacer algo con el agregado de
# los datos que has acumulado durante la ejecución del script.
2017-07-15 07:27:55 +00:00
END {
if (nlines)
2017-07-15 19:08:09 +00:00
print "La edad promedio para " name " es " sum / nlines
2017-07-15 07:27:55 +00:00
}
```
2017-07-15 19:08:09 +00:00
Más información:
2017-07-15 07:27:55 +00:00
2017-07-15 19:08:09 +00:00
* [Tutorial de AWK](http://www.grymoire.com/Unix/Awk.html)
* [Página man de AWK](https://linux.die.net/man/1/awk)
* [La guía del usuario de GNU Awk](https://www.gnu.org/software/gawk/manual/gawk.html):
GNU Awk se encuentra en la mayoría de los sistemas Linux.