Curso de Bash desde cero / Capítulo 12
Procesar texto
En el capítulo 4 aprendiste a encontrar líneas con
grep. Este capítulo va del paso siguiente:
transformarlas. Quedarte con una columna, ordenar,
contar cuántas veces se repite algo, sustituir texto.
Es lo que convierte un pipe en una respuesta. Con grep
sabes qué líneas hablan de errores; con lo de aquí sabes
qué usuario dio más errores y cuántos.
El texto como filas y columnas
Casi todo en Unix es texto plano en líneas: los registros del
sistema, la salida de ls -l, un CSV exportado de una hoja
de cálculo, la respuesta de un programa. Y todas las herramientas de
este capítulo comparten la misma idea:
- Leen una línea cada vez.
- La parten en campos (columnas) por un separador.
- Escriben el resultado en la salida estándar, para que lo recoja el siguiente comando del pipe.
Por eso encajan unas con otras. Ninguna hace mucho por sí sola; juntas hacen casi cualquier cosa.
Para todos los ejemplos vamos a usar el mismo archivo,
ventas.csv. Créalo para poder ir probando:
cat > ventas.csv << 'FIN'
fecha,vendedor,producto,unidades,precio
2026-07-01,Nica,teclado,3,25
2026-07-01,Marta,raton,5,12
2026-07-02,Leo,monitor,1,180
2026-07-02,Nica,raton,2,12
2026-07-03,Marta,teclado,4,25
2026-07-03,Nica,monitor,2,180
2026-07-04,Leo,raton,7,12
2026-07-04,Nica,teclado,1,25
FINNota
Ese
cat > archivo << 'FIN'es un heredoc: escribe en el archivo todo lo que venga hasta encontrar la palabraFINsola en una línea. Es la forma cómoda de crear un archivo de varias líneas desde la terminal.
Quedarse con una columna: cut
cut recorta trozos de cada línea. Su uso más habitual es
por campos:
-dindica el delimitador (el separador).-findica qué field (campo) quieres, empezando por 1.
cut -d',' -f2 ventas.csvY sale:
vendedor
Nica
Marta
Leo
Nica
Marta
Nica
Leo
NicaPuedes pedir varios campos, o un rango:
cut -d',' -f2,4 ventas.csv # el 2 y el 4
cut -d',' -f3- ventas.csv # del 3 hasta el final
cut -d',' -f-2 ventas.csv # desde el principio hasta el 2También sabe cortar por posición de carácter, con
-c:
cut -c1-10 ventas.csvY sale:
fecha,vend
2026-07-01
2026-07-01Fíjate en que la primera línea es la cabecera y se
cuela en todos los resultados. Para quitarla, tail -n +2
(«desde la línea 2 en adelante»):
tail -n +2 ventas.csv | cut -d',' -f2Ese tail -n +2 va a aparecer mucho en el capítulo.
Dónde falla cut
cut corta por un carácter separador. Si
el separador son espacios y hay un número variable de ellos, se
descoloca. El caso típico es ls -l, que alinea las columnas
rellenando con espacios:
ls -lY sale:
total 200
-rw-r--r-- 1 ndetomas ndetomas 200000 Jul 31 15:02 grande.bin
-rw-r--r-- 1 ndetomas ndetomas 5 Jul 31 15:02 pequeno.txtEl tamaño parece la quinta columna, así que uno diría
cut -d' ' -f5:
ls -l | cut -d' ' -f5Y sale:
200000
El primer archivo sale bien y el segundo sale vacío.
Porque en la línea de pequeno.txt el tamaño va precedido de
varios espacios, y para cut cada espacio abre un campo
nuevo: el quinto campo de esa línea es un espacio en blanco.
Hay dos soluciones. La primera, comprimir los espacios repetidos con
tr -s antes de cortar (lo verás dentro de un momento). La
segunda, usar awk, que trata los espacios seguidos como un
único separador y acierta a la primera:
ls -l | awk '{print $5}'Y sale:
200000
5Consejo
Regla rápida: si el separador es un carácter fijo (una coma, dos puntos, un tabulador),
cut. Si son espacios,awk.
Ordenar: sort
sort ordena las líneas. Por defecto,
alfabéticamente:
tail -n +2 ventas.csv | cut -d',' -f2 | sortY sale:
Leo
Leo
Marta
Marta
Nica
Nica
Nica
NicaY ahí está la trampa más famosa de sort:
alfabéticamente, el 1 de 100 va antes que el
9.
printf '10\n9\n100\n2\n' | sortY sale:
10
100
2
9Con -n (numérico) ordena como esperas:
printf '10\n9\n100\n2\n' | sort -nY sale:
2
9
10
100Las opciones que se usan de verdad:
| Opción | Qué hace |
|---|---|
-n |
Ordena como números, no como texto |
-r |
Al revés (de mayor a menor) |
-u |
Quita los repetidos (unique) |
-t |
Indica el separador de campos |
-k |
Ordena por un campo concreto, no por la línea entera |
-h |
Entiende tamaños tipo 4K, 2M,
1G |
Para ordenar el CSV por unidades vendidas, el separador es la coma
(-t',') y el campo es el cuarto:
tail -n +2 ventas.csv | sort -t',' -k4,4nY sale:
2026-07-02,Leo,monitor,1,180
2026-07-04,Nica,teclado,1,25
2026-07-02,Nica,raton,2,12
2026-07-03,Nica,monitor,2,180
2026-07-01,Nica,teclado,3,25
2026-07-03,Marta,teclado,4,25
2026-07-01,Marta,raton,5,12
2026-07-04,Leo,raton,7,12Nota
Se escribe
-k4,4ny no-k4npor un motivo:-k4significa «desde el campo 4 hasta el final de la línea».-k4,4significa «solo el campo 4». Lanpegada al final aplica el orden numérico a ese campo. Con-k4na veces sale bien por casualidad y otras no, según lo que haya en las columnas siguientes.
Contar repetidos: uniq
uniq quita líneas repetidas. Pero con una condición que
sorprende a todo el mundo la primera vez: solo compara líneas
contiguas.
printf 'Nica\nMarta\nNica\n' | uniqY sale:
Nica
Marta
NicaNo ha quitado nada, porque los dos Nica no estaban
pegados. Por eso uniq casi siempre va detrás de un
sort:
printf 'Nica\nMarta\nNica\n' | sort | uniqY sale:
Marta
NicaLo interesante viene con -c, que además
cuenta cuántas veces aparecía cada línea:
tail -n +2 ventas.csv | cut -d',' -f2 | sort | uniq -cY sale:
2 Leo
2 Marta
4 Nica| Opción | Qué hace |
|---|---|
-c |
Antepone el número de repeticiones |
-d |
Solo muestra las líneas que estaban repetidas |
-u |
Solo las que aparecían una única vez |
-i |
No distingue mayúsculas de minúsculas |
La receta del «top»
Esta combinación de tres comandos es de las más útiles que vas a aprender, y se usa igual para vendedores, para IPs de un registro o para tipos de error:
... | sort | uniq -c | sort -rnSe lee así: ordena (para que los iguales queden juntos), cuenta los grupos, y vuelve a ordenar por ese recuento de mayor a menor.
tail -n +2 ventas.csv | cut -d',' -f2 | sort | uniq -c | sort -rnY sale:
4 Nica
2 Marta
2 LeoAñade | head -10 al final y tienes un «top 10».
Sustituir y borrar caracteres: tr
tr (translate) trabaja carácter a
carácter, no con palabras. Recibe dos listas y cambia cada
carácter de la primera por el que le toca en la segunda.
echo "hola nica" | tr 'a-z' 'A-Z'Y sale:
HOLA NICASus tres usos habituales:
echo "1.234.567" | tr -d '.' # -d borra: 1234567
echo "hola mundo" | tr -s ' ' # -s comprime: hola mundo
head -2 ventas.csv | tr ',' '\n' # una columna por líneatr -s ' ' es la solución al problema de cut
con ls -l que veíamos antes:
ls -l | tr -s ' ' | cut -d' ' -f5Ahora sí devuelve los dos tamaños, porque los espacios repetidos se han convertido en uno solo.
Precaución
trno entiende de palabras.tr 'raton' 'RATON'no cambia la palabra «raton»: cambia cadarporR, cadaaporA, y así con cada letra, en cualquier parte del texto. Para sustituir palabras,sed.
Sustituir texto: sed
sed (stream editor) edita el texto según va
pasando. Tiene un lenguaje entero detrás, pero con cuatro cosas cubres
casi todo.
Sustituir
La orden s es la que se usa el 90% de las veces:
sed 's/raton/ratón/' ventas.csvSe lee: sustituye raton por
ratón. Las barras solo separan las partes.
Por defecto cambia una vez por línea. Con la
g final (global), todas:
echo "uno dos uno dos" | sed 's/uno/UNO/'
echo "uno dos uno dos" | sed 's/uno/UNO/g'Y sale:
UNO dos uno dos
UNO dos UNO dosBorrar líneas
La orden d borra:
sed '1d' ventas.csv # borra la línea 1 (la cabecera)
sed '/Leo/d' ventas.csv # borra las líneas que contengan "Leo"Mostrar solo lo que interesa
-n le dice a sed que no imprima nada por su
cuenta, y la orden p imprime lo que le pidas. Juntos son un
grep:
sed -n '/Nica/p' ventas.csv
sed -n '2,4p' ventas.csv # solo las líneas 2 a 4Editar el archivo de verdad
Todo lo anterior muestra el resultado por pantalla y no toca
el archivo. Para modificarlo está -i (in
place).
Precaución
sed -ireescribe el archivo sin preguntar y sin papelera. Si la sustitución estaba mal, el original ya no existe.Dos costumbres que evitan el disgusto: ejecuta siempre primero sin
-ipara ver qué saldría, y usa-i.bak, que guarda una copia del original antes de tocarlo.
sed 's/raton/ratón/g' ventas.csv # 1. mirar el resultado
sed -i.bak 's/raton/ratón/g' ventas.csv # 2. aplicarlo, con copia de seguridadDespués de eso tienes el archivo cambiado y un
ventas.csv.bak con el original intacto.
Columnas de verdad: awk
awk es el más potente de todos, y en realidad es un
lenguaje de programación completo. Pero se puede usar como una
herramienta más, aprendiendo cuatro cosas.
Su idea básica: por cada línea, awk la parte en campos y
les pone nombre.
| Variable | Qué es |
|---|---|
$1, $2, $3… |
El primer campo, el segundo, el tercero… |
$0 |
La línea entera |
NF |
El número de fields (cuántas columnas tiene la línea) |
NR |
El número de registro: por qué línea va |
Por defecto separa por espacios (y le dan igual los espacios
repetidos, de ahí que acertara con ls -l). Con
-F le indicas otro separador:
awk -F',' '{print $2}' ventas.csv
awk -F',' '{print $2, $4}' ventas.csvY sale:
vendedor unidades
Nica 3
Marta 5
Leo 1La coma dentro del print mete un espacio entre los dos
valores.
Filtrar
Antes de las llaves puedes poner una condición, y
awk solo aplica el bloque a las líneas que la cumplen:
awk -F',' '$4 > 3 {print $2, $3, $4}' ventas.csvY sale:
vendedor producto unidades
Marta raton 5
Marta teclado 4
Leo raton 7La cabecera se ha colado. No es un error de awk: al
comparar el texto unidades con el número 3,
awk los compara como texto, y unidades
alfabéticamente va después de 3. Se arregla exigiendo
además que no sea la primera línea:
awk -F',' 'NR > 1 && $4 > 3 {print $2, $3, $4}' ventas.csvY sale:
Marta raton 5
Marta teclado 4
Leo raton 7También puedes filtrar por texto, entre barras, como en
grep:
awk -F',' '/Leo/ {print $0}' ventas.csvCalcular
Aquí es donde awk se separa del resto: sabe hacer
cuentas.
awk -F',' 'NR > 1 {print $3, $4 * $5}' ventas.csvY sale:
teclado 75
raton 60
monitor 180
raton 24
teclado 100
monitor 360
raton 84
teclado 25Y con el bloque END, que se ejecuta una sola vez
al terminar, puedes acumular totales:
awk -F',' 'NR > 1 {total += $4} END {print "Total unidades:", total}' ventas.csvY sale:
Total unidades: 25Nota
No hace falta declarar
totalni ponerlo a cero:awksupone que una variable nueva usada en una suma empieza valiendo 0. Es una de las pocas cosas en las que es más cómodo que Bash.
Todo junto
Vamos con una pregunta de verdad: ¿qué vendedor ha ingresado más dinero?
No hay un comando que lo responda. Hay que construirlo por tramos, y la forma de hacerlo es ir añadiendo un comando cada vez y mirar la salida.
Primero, quitar la cabecera:
tail -n +2 ventas.csvAhora, por cada línea, el vendedor y lo que facturó (unidades por precio):
tail -n +2 ventas.csv | awk -F',' '{print $2, $4 * $5}'Y sale:
Nica 75
Marta 60
Leo 180
Nica 24
Marta 100
Nica 360
Leo 84
Nica 25Falta sumar por vendedor. awk puede guardar totales
separados usando el nombre como índice, y volcarlos al final:
tail -n +2 ventas.csv \
| awk -F',' '{suma[$2] += $4 * $5} END {for (v in suma) print suma[v], v}'Y sale:
Marta 160
Leo 264
Nica 484Solo queda ordenar de mayor a menor:
tail -n +2 ventas.csv \
| awk -F',' '{suma[$2] += $4 * $5} END {for (v in suma) print suma[v], v}' \
| sort -rnY sale:
484 Nica
264 Leo
160 MartaFíjate en dos detalles del resultado. El primero, que se imprime
suma[v], v (primero el número y luego el nombre)
precisamente para poder ordenar después:
sort -rn mira el principio de la línea. El segundo, que la
barra invertida al final de la línea permite partir un pipe largo en
varias líneas para que se lea mejor.
Cuándo usar cuál
| Herramienta | Úsala para |
|---|---|
cut |
Sacar columnas cuando el separador es un carácter fijo |
sort |
Ordenar, y para preparar la entrada de uniq |
uniq |
Contar repeticiones (siempre después de sort) |
tr |
Cambiar o borrar caracteres sueltos, comprimir espacios |
sed |
Sustituir o borrar texto línea a línea |
awk |
Columnas separadas por espacios, filtros con condiciones y cuentas |
La regla práctica: empieza por lo simple. Si cut te
vale, usa cut. Cuando necesites una condición o una suma,
pasa a awk.
Ejercicios
Muestra la lista de productos vendidos, sin repetir y ordenada alfabéticamente.
Averigua cuál es el producto que más veces aparece en el archivo, usando la receta del «top».
Calcula el total de dinero facturado en todo el archivo (unidades × precio, sumando todas las líneas).
Saca las ventas del día 2026-07-03 mostrando solo vendedor y producto, separados por un guion en vez de por una coma.
Soluciones
1. Cortar la columna del producto, quitar la cabecera y ordenar sin repetidos:
tail -n +2 ventas.csv | cut -d',' -f3 | sort -u2. La misma columna, pero con la receta completa:
tail -n +2 ventas.csv | cut -d',' -f3 | sort | uniq -c | sort -rn | head -13. Es la suma del capítulo, pero multiplicando antes:
awk -F',' 'NR > 1 {total += $4 * $5} END {print "Facturado:", total}' ventas.csv4. Se filtra por la fecha y se imprime con el
separador que quieras. La variable OFS (output field
separator) es lo que awk pone entre los valores del
print:
awk -F',' -v OFS=' - ' '/2026-07-03/ {print $2, $3}' ventas.csvTambién vale sin OFS, escribiendo el guion a mano:
awk -F',' '/2026-07-03/ {print $2 " - " $3}' ventas.csv