Estos tres comandos resuelven un problema muy concreto: dado un archivo de texto estructurado —un log, un CSV, un /etc/passwd— necesitas extraer columnas específicas, ordenarlas y contar frecuencias sin escribir ni una línea de Python. Son herramientas de los años 70 que siguen siendo la forma más rápida de responder preguntas sobre datos tabulares desde la línea de comandos.
cut extrae fragmentos de cada línea: bien por delimitador de campo (-d + -f), bien por posición de carácter (-c). La decisión de diseño importante aquí es que cut trata cada delimitador de forma literal e independiente. Si tienes root::0 en un archivo, el doble :: produce un campo vacío entre los dos separadores — no los colapsa. Esto contrasta con awk, que por defecto agrupa cualquier cantidad de espacios o tabuladores como un único separador. Cuando el delimitador es espacio y los campos no están perfectamente alineados, cut te dará resultados incorrectos; awk no. Úsalos según la situación.
sort reordena líneas. Sin flags, la ordenación es lexicográfica, lo que significa que 10 aparece antes que 2 porque el carácter 1 precede al 2 en ASCII. Cuando los datos son números, -n es obligatorio. Con -k especificas sobre qué campo ordenar; la sintaxis -k2,2n (“desde el campo 2 hasta el campo 2, numérico”) evita que sort continúe comparando campos siguientes cuando el campo primario empata.
uniq elimina líneas duplicadas adyacentes. Si el archivo no está ordenado, dos líneas idénticas separadas por una tercera diferente no se colapsan. Por eso la combinación canónica es siempre sort archivo | uniq: primero juntamos los iguales, luego los deduplicamos. Con -c añade el conteo delante de cada línea única, que es la base del patrón de análisis de logs más usado en administración de sistemas.
Escenario: las 10 IPs que más golpean el servidor web
Tenemos un access.log de nginx con formato combined y queremos saber quién nos está machacando, en tiempo real. De paso, vamos a explorar los tres comandos sobre el propio /etc/passwd antes de llegar al log.
# ── Exploración con /etc/passwd ──────────────────────────────────────────
# Extraer solo los nombres de usuario (campo 1, delimitador :)
cut -d: -f1 /etc/passwd
# Campos 1 y 3 (usuario y UID) — la coma selecciona campos no contiguos
cut -d: -f1,3 /etc/passwd
# Campos 1 a 5 — el guion especifica un rango continuo
cut -d: -f1-5 /etc/passwd
# Extraer los primeros 10 caracteres de cada línea (modo posición)
# Útil para logs con timestamp fijo al inicio
cut -c1-10 /var/log/dpkg.log
# Demostración de la trampa del delimitador doble:
# Esta línea tiene :: entre contraseña y UID, produciendo campo 2 vacío
echo "root::0:0:root:/root:/bin/bash" | cut -d: -f2 # imprime: (vacío)
echo "root::0:0:root:/root:/bin/bash" | cut -d: -f3 # imprime: 0
# ── Ordenación ───────────────────────────────────────────────────────────
# Ordenar usuarios alfabéticamente
cut -d: -f1 /etc/passwd | sort
# Ordenar por UID (campo 3) de forma numérica
# Sin -n, el UID 10 aparecería antes que el 2
cut -d: -f1,3 /etc/passwd | sort -t: -k2,2n
# Mostrar los 5 UIDs más altos (orden numérico inverso)
cut -d: -f1,3 /etc/passwd | sort -t: -k2,2n -r | head -5
# ── uniq y conteo ────────────────────────────────────────────────────────
# Shells únicas usadas en el sistema
cut -d: -f7 /etc/passwd | sort | uniq
# Cuántas cuentas usan cada shell — uniq -c añade el conteo al inicio
cut -d: -f7 /etc/passwd | sort | uniq -c
# Ordenado por frecuencia descendente
cut -d: -f7 /etc/passwd | sort | uniq -c | sort -rn
# ── Patrón clásico: top 10 IPs en access.log ────────────────────────────
# El campo 1 en formato combined de nginx/Apache es la IP del cliente.
# awk en lugar de cut porque los campos están separados por espacios
# variables (cut con -d' ' fallaría si hay doble espacio).
awk '{print $1}' /var/log/nginx/access.log \
| sort \
| uniq -c \
| sort -rn \
| head -10
# Si quieres solo las IPs sin el conteo (por ejemplo, para meterlas en nftables):
awk '{print $1}' /var/log/nginx/access.log \
| sort \
| uniq -c \
| sort -rn \
| head -10 \
| awk '{print $2}' # el conteo queda en $1, la IP en $2 tras uniq -c
Qué está pasando en cada decisión
-d: -f1,3 vs -f1-3: la coma lista campos individuales; el guion especifica un rango. Son ortogonales y se pueden combinar: -f1,3-5,7 es válido. En el ejemplo de /etc/passwd, usamos -f1,3 para saltar el campo de contraseña (campo 2) que en sistemas modernos siempre es x.
sort -t: -k2,2n: cuando los datos ya tienen un delimitador distinto al espacio (como los : de passwd), sort necesita -t: para saber cómo partir los campos — exactamente igual que cut necesita -d:. El 2,2 en -k2,2n le dice “ordena comparando desde el campo 2 hasta el campo 2 inclusive, en modo numérico”. Si escribieras -k2n sin el segundo 2, sort seguiría comparando el resto de la línea como desempate, lo que puede producir resultados sorprendentes con UIDs repetidos entre archivos.
Por qué awk '{print $1}' en lugar de cut -d' ' -f1: en el log de nginx los campos están separados por un espacio simple, y en teoría cut funcionaría. Pero en la práctica, cualquier variación en el formato (doble espacio, logs rotados con cabeceras distintas) rompe cut silenciosamente. awk colapsa cualquier cantidad de espacios y tabuladores, lo que lo hace más robusto para texto que no controlas tú.
El orden del pipeline final: sort antes de uniq es imprescindible — ya explicado. El segundo sort -rn actúa sobre las líneas que uniq -c produce, donde el número de ocurrencias está al inicio. El -r invierte para mostrar primero las IPs más activas, y el -n es obligatorio porque sin él 9 aparecería después de 10 — el mismo problema de ordenación lexicográfica de siempre.
head -10 al final: corta el pipeline en cuanto tiene 10 líneas, lo que hace que los procesos anteriores reciban SIGPIPE y terminen. En logs de varios gigabytes, esto es lo que evita que la operación tarde minutos en vez de segundos.
N° 49