detomas.net Curso de Bash desde cero
Descargar PDF

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:

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
FIN

Nota

Ese cat > archivo << 'FIN' es un heredoc: escribe en el archivo todo lo que venga hasta encontrar la palabra FIN sola 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:

cut -d',' -f2 ventas.csv

Y sale:

vendedor
Nica
Marta
Leo
Nica
Marta
Nica
Leo
Nica

Puedes 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 2

También sabe cortar por posición de carácter, con -c:

cut -c1-10 ventas.csv

Y sale:

fecha,vend
2026-07-01
2026-07-01

Fí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',' -f2

Ese 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 -l

Y 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.txt

El tamaño parece la quinta columna, así que uno diría cut -d' ' -f5:

ls -l | cut -d' ' -f5

Y 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
5

Consejo

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 | sort

Y sale:

Leo
Leo
Marta
Marta
Nica
Nica
Nica
Nica

Y 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' | sort

Y sale:

10
100
2
9

Con -n (numérico) ordena como esperas:

printf '10\n9\n100\n2\n' | sort -n

Y sale:

2
9
10
100

Las 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,4n

Y 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,12

Nota

Se escribe -k4,4n y no -k4n por un motivo: -k4 significa «desde el campo 4 hasta el final de la línea». -k4,4 significa «solo el campo 4». La n pegada al final aplica el orden numérico a ese campo. Con -k4n a 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' | uniq

Y sale:

Nica
Marta
Nica

No 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 | uniq

Y sale:

Marta
Nica

Lo 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 -c

Y 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 -rn

Se 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 -rn

Y sale:

      4 Nica
      2 Marta
      2 Leo

Añ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 NICA

Sus 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ínea

tr -s ' ' es la solución al problema de cut con ls -l que veíamos antes:

ls -l | tr -s ' ' | cut -d' ' -f5

Ahora sí devuelve los dos tamaños, porque los espacios repetidos se han convertido en uno solo.

Precaución

tr no entiende de palabras. tr 'raton' 'RATON' no cambia la palabra «raton»: cambia cada r por R, cada a por A, 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.csv

Se 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 dos

Borrar 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 4

Editar 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 -i reescribe 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 -i para 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 seguridad

Despué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.csv

Y sale:

vendedor unidades
Nica 3
Marta 5
Leo 1

La 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.csv

Y sale:

vendedor producto unidades
Marta raton 5
Marta teclado 4
Leo raton 7

La 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.csv

Y sale:

Marta raton 5
Marta teclado 4
Leo raton 7

También puedes filtrar por texto, entre barras, como en grep:

awk -F',' '/Leo/ {print $0}' ventas.csv

Calcular

Aquí es donde awk se separa del resto: sabe hacer cuentas.

awk -F',' 'NR > 1 {print $3, $4 * $5}' ventas.csv

Y sale:

teclado 75
raton 60
monitor 180
raton 24
teclado 100
monitor 360
raton 84
teclado 25

Y 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.csv

Y sale:

Total unidades: 25

Nota

No hace falta declarar total ni ponerlo a cero: awk supone 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.csv

Ahora, 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 25

Falta 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 484

Solo 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 -rn

Y sale:

484 Nica
264 Leo
160 Marta

Fí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

  1. Muestra la lista de productos vendidos, sin repetir y ordenada alfabéticamente.

  2. Averigua cuál es el producto que más veces aparece en el archivo, usando la receta del «top».

  3. Calcula el total de dinero facturado en todo el archivo (unidades × precio, sumando todas las líneas).

  4. 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 -u

2. La misma columna, pero con la receta completa:

tail -n +2 ventas.csv | cut -d',' -f3 | sort | uniq -c | sort -rn | head -1

3. Es la suma del capítulo, pero multiplicando antes:

awk -F',' 'NR > 1 {total += $4 * $5} END {print "Facturado:", total}' ventas.csv

4. 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.csv

También vale sin OFS, escribiendo el guion a mano:

awk -F',' '/2026-07-03/ {print $2 " - " $3}' ventas.csv