Contar palabras y caracteres en Linux: wc y todo lo que viene después

Rubén Castro, 30 agosto 2026
contar palabras linux

Contar palabras en la terminal parece el ejercicio más aburrido del mundo hasta que lo necesitas de verdad: comprobar si un artículo llega al mínimo que te han pedido, saber cuántas líneas tiene un registro antes de abrirlo, averiguar qué palabra repites hasta la náusea en todo lo que escribes.

El comando de siempre es wc, y en treinta segundos se aprende. Lo interesante empieza después, cuando lo enchufas a grep, sort y awk y de pronto puedes preguntarle cosas a un libro entero.

Hay un problema con casi todas las recetas que vas a encontrar por ahí: están escritas y probadas en inglés. En cuanto las lanzas contra un texto en español, parten «corazón» por la mitad, se inventan palabras que no existen y te dejan una tabla de frecuencias que parece correcta y no lo es. Este artículo enseña las dos cosas: los comandos, y dónde te van a mentir.

Todos los números de este artículo están medidos, no copiados. Para que puedas reproducirlos exactamente, todos salen del mismo fichero: el Quijote completo del Proyecto Gutenberg, que es de dominio público y pesa 2,2 MB.

curl -o quijote.txt https://www.gutenberg.org/cache/epub/2000/pg2000.txt

Si tus cifras no coinciden con las de aquí, no es cosa tuya: Gutenberg actualiza sus ficheros de vez en cuando.

wc, los cinco números que sabe dar

wc viene de word count, pero cuenta bastante más que palabras. Lanzado a secas escupe tres cifras y el nombre del fichero:

wc quijote.txt
  38059  389656 2225803 quijote.txt

Son, por este orden, líneas, palabras y bytes. Casi 390.000 palabras y 2,2 millones de bytes.

Si solo quieres una, se la pides:

Las opciones de wc

OpciónQué cuentaEn el Quijote
-lLíneas38.059
-wPalabras389.656
-cBytes2.225.803
-mCaracteres2.168.116
-LAncho de la línea más larga78

Un par de detalles que ahorran disgustos:

  • -w cuenta lo que hay entre espacios, no palabras del diccionario. «Don Quijote de la Mancha» son cinco. Y IP-2 es una sola.
  • El orden de las opciones no cambia el orden de la salida. Si escribes wc -c -m fichero esperando bytes y luego caracteres, te llevas una sorpresa: wc siempre imprime en su orden fijo —líneas, palabras, caracteres, bytes, línea más larga— pongas los flags como los pongas. Es una fuente de errores estupenda dentro de un script.
printf "camión" | wc -c -m
      6       7

Seis y siete, no siete y seis: primero los caracteres (-m) y después los bytes (-c). Y esa diferencia de uno es justo el tema del capítulo siguiente.

Bytes y caracteres no son lo mismo: 57.687 de diferencia

Aquí es donde el español se separa del inglés. Pide las dos cifras del Quijote:

wc -c quijote.txt   # 2225803 bytes
wc -m quijote.txt   # 2168116 caracteres

57.687 bytes de diferencia. En un texto en inglés esa resta daría cero o casi. Aquí no, porque en UTF-8 las letras que llevan tilde, la eñe y los signos de apertura no caben en un byte:

printf "año" | wc -c -m      # 3 caracteres, 4 bytes
printf "corazón" | wc -c -m  # 7 caracteres, 8 bytes

En todo el libro hay 50.712 caracteres que no son ASCII, y entre todos ocupan 108.399 bytes. Estos son los que más se repiten:

Los caracteres que engordan el fichero

CarácterVecesBytes que ocupa
í12.4642
ó9.2242
á7.1362
é7.1042
— (raya de diálogo)6.9253
ñ4.2722
ú1.2722
¿9592
¡6852

La mayoría pesan dos bytes, pero la raya de diálogo —esa que abre los diálogos del Quijote casi siete mil veces— pesa tres. Y hay casos peores: la bandera de España es un solo emoji para ti y ocho bytes para el disco.

printf "🇪🇸" | wc -c -m
      2       8

Dos caracteres, porque un emoji de bandera son en realidad dos símbolos superpuestos.

Cuál de las dos usar. Si lo que te importa es espacio en disco, tamaño de subida o un límite de la base de datos, cuenta bytes con -c. Si lo que te importa es cuánto texto hay —un límite de 280 caracteres, el titular de una ficha—, cuenta caracteres con -m. Confundirlas es la razón por la que un texto de 140 caracteres a veces no entra en un campo de 140.

El carácter invisible que descuadra las cuentas

Pregúntale a wc por la línea más larga del libro y luego pregúntaselo a awk. No dicen lo mismo:

wc -L quijote.txt
# 78

awk '{ if (length($0) > m) { m = length($0); l = NR } } END { print l, m }' quijote.txt
# 945 79

Setenta y ocho contra setenta y nueve. Uno de los dos sobra, y el culpable no se ve:

sed -n '945p' quijote.txt | cat -A
GANDALÍN, ESCUDERO DE AMADÍS DE GAULA, A SANCHO PANZA, ESCUDERO DE DON QUIJOTE^M$

Ese ^M del final es un retorno de carro, el \r. El fichero viene con saltos de línea de Windows (CRLF) en vez de los de Unix (LF), así que cada línea arrastra un carácter de más. awk lo cuenta porque forma parte de la línea; wc -L no, porque mide el ancho en pantalla y el retorno de carro no ocupa nada.

Son 38.059 retornos de carro, uno por línea, y cada uno es un byte que no pinta nada:

tr -cd '\r' < quijote.txt | wc -c
# 38059

Se limpia de una pasada:

tr -d '\r' < quijote.txt > quijote-limpio.txt

Y a partir de ahí las dos herramientas se ponen de acuerdo en 78.

Esto no es una rareza del Quijote. Cualquier texto que haya pasado por Windows —un CSV exportado de Excel, un .txt que te ha mandado un cliente, un fichero de un repositorio con la configuración de saltos de línea mal puesta— trae lo mismo. Si un recuento te sale sistemáticamente uno más de lo que esperas, empieza mirando aquí.

Quítale el nombre del fichero a la respuesta

Esto parece una tontería y se convierte en un fastidio en cuanto metes wc dentro de un script:

wc -l quijote.txt
# 38059 quijote.txt

wc -l < quijote.txt
# 38059

La diferencia es quién abre el fichero. En el primer caso se lo pasas como argumento, wc sabe cómo se llama y lo imprime. En el segundo se lo das ya abierto por la entrada estándar: no tiene ni idea de dónde viene, así que solo puede darte el número.

Cuando la cifra va a parar a una variable, la segunda forma es la que quieres:

lineas=$(wc -l < quijote.txt)
echo "El fichero tiene $lineas líneas"

Con la primera te habrías llevado 38059 quijote.txt dentro de la variable, y la siguiente comparación numérica habría reventado.

Con varios ficheros a la vez añade un total:

wc -l /etc/hosts /etc/passwd
   7 /etc/hosts
  42 /etc/passwd
  49 total

Ojo con ese total si luego procesas la salida: es una línea más que no es ningún fichero.

Y para el largo de un texto que ya tienes en una variable no hace falta wc ni tubería ninguna, lo hace el propio shell:

palabra="camión"
echo "${#palabra}"   # 6

Contar apariciones no es contar líneas

Cuántas veces sale Sancho en el libro. Parece una pregunta con una sola respuesta y tiene dos:

grep -c "Sancho" quijote.txt
# 2149

grep -o "Sancho" quijote.txt | wc -l
# 2173

Veinticuatro de diferencia, y ninguno de los dos está mal: cuentan cosas distintas. grep -c cuenta líneas que contienen la palabra, y una línea con dos Sanchos suma uno. grep -o imprime cada aparición en su propia línea, así que la misma línea suma dos.

Se ve mejor en pequeño:

printf 'Sancho y Sancho\notra linea\n' > mini.txt

grep -c "Sancho" mini.txt          # 1
grep -o "Sancho" mini.txt | wc -l  # 2

La regla práctica: si la pregunta es «¿en cuántos sitios aparece?», -c. Si es «¿cuántas veces se dice?», -o y a contar. En un registro de servidor esa diferencia es la que hay entre «cuántas peticiones fallaron» y «cuántas veces apareció el error», que rara vez es el mismo número.

Las palabras más repetidas (y por qué la receta inglesa miente)

Esta es la tubería que circula por medio internet para sacar el ranking de palabras de un texto:

tr -sc '[:alpha:]' '\n' < quijote.txt | tr '[:upper:]' '[:lower:]' \
  | sort | uniq -c | sort -rn | head

Va por partes: tr -sc '[:alpha:]' '\n' cambia todo lo que no es una letra por un salto de línea, de forma que queda una palabra por renglón; el segundo tr pasa a minúsculas; sort las agrupa; uniq -c cuenta cada grupo; y sort -rn ordena de mayor a menor.

En inglés funciona perfectamente. En español devuelve esto:

La misma pregunta, dos respuestas

PuestoCon tr (byte a byte)Con grep (respeta el idioma)
1que — 20.769que — 20.769
2de — 18.411de — 18.411
3y — 18.280y — 18.272
4a — 15.700la — 10.492
5la — 10.528a — 9.934
6en — 8.295en — 8.285
7el — 8.271el — 8.265
8se — 7.035no — 6.356
9no — 6.397los — 4.769
10los — 4.776se — 4.752
11con — 4.275con — 4.275
12n — 4.195 ⚠️por — 3.945

Fíjate en la columna de la izquierda. Ahí abajo hay una «n» suelta que aparece 4.195 veces, y «n» no es una palabra en español. Tampoco cuadran «a» ni «se».

El motivo es que tr trabaja byte a byte y no entiende UTF-8. Para él, los dos bytes de una «ó» no son letras, así que los trata como separadores y parte la palabra en dos:

printf 'corazón\n' | tr -sc '[:alpha:]' '\n'
coraz
n

De «corazón» salen «coraz» y «n». Y como el español está lleno de palabras acabadas en -ón, -án y -én —razón, también, capitán, corazón—, esa «n» huérfana se repite miles de veces. Con «año» es aún peor: la eñe desaparece del todo y quedan «a» y «o».

La solución es cambiar la herramienta que corta, no la que cuenta. grep sí respeta el idioma configurado en el sistema:

grep -oE '[[:alpha:]]+' quijote.txt | tr '[:upper:]' '[:lower:]' \
  | sort | uniq -c | sort -rn | head

Con esa versión la «n» fantasma desaparece —comprobado: cero apariciones— y «señor» pasa de invisible a estar entre las palabras más usadas del libro.

La segunda trampa: tr tampoco sabe poner minúsculas

Ya hemos arreglado el corte de palabras, pero en la tubería de arriba queda un tr más, el que pasa a minúsculas. Y falla exactamente por lo mismo:

printf 'ÁNGEL ESPAÑA Ñ\n' | tr '[:upper:]' '[:lower:]'
Ángel espaÑa Ñ

Ha bajado las letras normales y ha dejado la «Á» y la «Ñ» tal cual. El resultado no es solo feo: en la tabla de frecuencias, «Ángel» y «ángel» cuentan como dos palabras distintas. En el Quijote pasa 178 veces, repartidas en 40 palabras distintas: «Éste» treinta y siete veces, «Él» veintinueve, «Álvaro» veinte.

Cualquiera de estas dos sí lo hace bien:

sed 's/.*/\L&/'          # GNU sed
awk '{ print tolower($0) }'  # gawk
printf 'ÁNGEL ESPAÑA Ñ\n' | sed 's/.*/\L&/'
# ángel españa ñ

Con las dos piezas cambiadas, la tubería que sí funciona en español queda así:

grep -oE '[[:alpha:]]+' quijote.txt \
  | sed 's/.*/\L&/' \
  | sort | uniq -c | sort -rn | head
La regla que se saca de estos dos capítulos vale para casi todo: tr es de bytes; grep, sed y awk son de caracteres. Para tocar texto en cualquier idioma que no sea inglés, tr solo sirve para trabajos que no dependan del alfabeto —borrar retornos de carro, por ejemplo—.

Frecuencia de letras y de pares de letras

Cambiando el + por nada en la expresión, cada letra sale por su cuenta y tienes la frecuencia del alfabeto:

grep -oE '[[:alpha:]]' quijote.txt | sed 's/.*/\L&/' \
  | sort | uniq -c | sort -rn | head

Las letras más frecuentes del Quijote

LetraVeces
e225.257
a195.635
o155.785
s127.289
n110.260
r102.719
l90.168
d88.476
u79.300
i79.194

E, A, O, S, N, R. Es el orden clásico del español y explica por qué la Ñ, que tiene un lugar de honor en el idioma, aparece cuatro mil veces frente a las doscientas veinticinco mil de la E.

Los pares de letras (bigramas) hacen falta un poco más de maña, porque hay que recorrer cada palabra deslizando una ventana de dos:

grep -oE '[[:alpha:]]+' quijote.txt | sed 's/.*/\L&/' \
  | awk '{ for (i = 1; i < length($0); i++) print substr($0, i, 2) }' \
  | sort | uniq -c | sort -rn | head

Los pares de letras más frecuentes

ParVeces
ue37.207
de34.487
en34.102
es32.903
qu32.725
er27.606
os25.773
la23.321
do21.821
an21.684

El podio dice mucho del idioma: «ue» y «qu» están altísimos por culpa de una sola palabra, «que», que es también la más repetida del libro con 20.769 apariciones.

Y tarda menos de un segundo en recorrer dos millones de caracteres, que para un awk de una línea no está nada mal.

Palabras de cinco letras, o cómo hacer trampas al Wordle

awk filtra por longitud, así que sacar las palabras de exactamente cinco letras es añadir una condición:

grep -oE '[[:alpha:]]+' quijote.txt | sed 's/.*/\L&/' \
  | awk 'length($0) == 5' \
  | sort | uniq -c | sort -rn | head

Palabras de cinco letras más frecuentes

PalabraVeces
señor1.065
había1.037
todos818
donde722
quien621
decir578
hacer535
aquel487
sobre456
tanto421

Como diccionario de Wordle el Quijote es un poco tramposo —«señor» y «había» salen tantas veces por el tipo de libro que es—, pero la técnica sirve para cualquier corpus. Cambia el 5 por el número que quieras y tienes las palabras de esa longitud ordenadas por uso real, que es bastante mejor guía que una lista alfabética.

Si el filtro lo quieres por otra cosa, awk acepta lo que se te ocurra:

awk 'length($0) >= 12'              # palabras largas
awk '/^des/'                        # las que empiezan por des-
awk 'length($0) == 5 && /a/'        # de cinco letras y con una a

LC_ALL=C: cuatro veces más rápido y te destroza el español

En cuanto buscas cómo acelerar estas tuberías aparece siempre el mismo consejo: ponle LC_ALL=C delante. Y es verdad que corre. Sobre el Quijote, la misma tubería:

Lo que cambia el idioma configurado

Cómo se lanzaTiempo¿Cuenta bien el español?
Con el idioma del sistema358 ms
Todo con LC_ALL=C83 msNo — pierde tildes y eñes
Cortar con idioma + ordenar en C156 ms

Más de cuatro veces más rápido. Lo que casi nadie añade es lo que estás pagando: LC_ALL=C le dice al sistema que se olvide del idioma y trate el texto como bytes sueltos, que es exactamente el problema que acabamos de arreglar dos capítulos más arriba.

printf 'corazón\n' | LC_ALL=C grep -oE '[[:alpha:]]+'
coraz
n

Vuelve la «n» fantasma, otras 4.181 veces. Y «señor», que aparece 1.065 veces en el libro, pasa a aparecer cero.

La forma correcta de quedarse con lo bueno es separar los dos trabajos: cortar las palabras respetando el idioma, y ordenarlas sin él. A sort y a uniq les da igual el alfabeto mientras agrupen lo idéntico:

grep -oE '[[:alpha:]]+' quijote.txt \
  | sed 's/.*/\L&/' \
  | LC_ALL=C sort | LC_ALL=C uniq -c | LC_ALL=C sort -rn \
  | head

0,156 segundos y los resultados intactos: «que» 20.769, «de» 18.411, «y» 18.272. Más del doble de rápido que la versión ingenua y sin romper nada.

Contar ficheros sin que te engañen

El clásico para saber cuántos ficheros hay en una carpeta:

ls /etc | wc -l
# 247

Y está mal por dos motivos. El primero es que cuenta también las carpetas; los ficheros de verdad son la mitad:

find /etc -maxdepth 1 -type f | wc -l
# 121

El segundo es más traicionero: wc -l cuenta saltos de línea, así que un fichero cuyo nombre contenga un salto de línea cuenta por dos. Suena imposible hasta que alguien descomprime un ZIP raro en tu servidor. La versión a prueba de nombres imposibles no cuenta líneas, cuenta puntos:

find /etc -maxdepth 1 -type f -printf '.' | wc -c
# 121

find imprime un punto por cada fichero encontrado, sin saltos de línea de por medio, y wc -c cuenta los puntos. El nombre ya no participa.

Quitando el -maxdepth 1 cuentas el árbol entero, y con -name filtras por extensión:

find . -type f -name '*.md' -printf '.' | wc -c

Contar las palabras de lo que escribes tú

Este es el uso que más veces acabas dándole. Un artículo en Markdown, contado en bruto:

cat content/*.md | wc -w
# 1928

Ese número está inflado, porque ahí dentro van las cabeceras del fichero y las etiquetas de la plantilla, que no son texto que nadie vaya a leer. Quitándolas:

cat content/*.md \
  | sed '/^---$/,/^---$/d' \
  | sed -E 's/\{\{[<%][^}]*[>%]\}\}//g' \
  | grep -oE '[[:alpha:]]+' | wc -l
# 1747

181 palabras de diferencia, casi un 10 %. Si escribes para alguien que paga por palabra o exige un mínimo, la distinción no es académica.

Y para ver el reparto por capítulos, wc acepta comodines:

wc -w content/*.md

Los comandos que más usas salen del historial. En Bash el historial es un fichero de texto plano, así que basta con contarlo:

cut -d' ' -f1 ~/.bash_history | sort | uniq -c | sort -rn | head

En Zsh las líneas llevan una marca de tiempo delante que hay que quitar antes:

sed 's/^: [0-9]*:[0-9]*;//' ~/.zsh_history | cut -d' ' -f1 \
  | sort | uniq -c | sort -rn | head

Y en Fish el historial es un YAML, con el comando detrás de - cmd::

grep '^- cmd: ' ~/.local/share/fish/fish_history \
  | awk '{ print $3 }' | sort | uniq -c | sort -rn | head

Chuleta

Todo lo anterior en una tabla, para cuando vuelvas dentro de seis meses sin acordarte de nada:

Recetas para contar en Linux

Lo que quieresEl comando
Líneas de un ficherowc -l fichero
Solo el número sin el nombrewc -l < fichero
Palabraswc -w fichero
Caracteres (tildes incluidas)wc -m fichero
Bytes en discowc -c fichero
Largo de una variable${#variable}
Líneas que contienen algogrep -c patrón fichero
Veces que aparece algogrep -o patrón fichero | wc -l
Ficheros de una carpetafind . -maxdepth 1 -type f -printf '.' | wc -c
Palabras más repetidasgrep -oE '[[:alpha:]]+' f | sed 's/.*/\L&/' | sort | uniq -c | sort -rn
Quitar los saltos de línea de Windowstr -d '\r' < entrada > salida

Y las tres reglas que evitan el 90 % de los sustos:

  1. tr no entiende acentos. Para cortar palabras usa grep -oE '[[:alpha:]]+'; para pasar a minúsculas, sed 's/.*/\L&/'.
  2. LC_ALL=C es para ordenar, no para cortar. Ponlo en sort y uniq, nunca en el grep que separa las palabras.
  3. Si un número no te cuadra por poco, busca lo invisible: un retorno de carro, un total de más al final de la salida o el nombre del fichero colado en la respuesta.
Rubén Castro

Rubén Castro

Redactor

Apasionado de explorar y diseccionar lo último en tecnología. Tengo mucha experiencia en el mundo de los ordenadores y el gaming, aunque también me gustan todos los tipos de gadgets.