Redirecciones y tuberías en Linux — ejercicios con intentos fallidos, rankings que se parten y un 2>&1 en el sitio equivocado (y un grep que contaba cero errores con la pantalla llena de ellos)
Una tarde, compilando la práctica de C, quise saber cuántos errores me daba gcc sin tener que contarlos con el dedo. Escribí gcc programa.c | grep -c error y la terminal me respondió con la pantalla llena de errores… y, al final, un 0. Cero errores, decía grep, mientras veía quince. Tardé un rato en entenderlo: gcc escribe sus errores por stderr, y por la tubería solo viaja stdout. grep no recibió nada y contó, con toda la razón, cero. Con 2>&1 delante de la barra salió el número de verdad. Estos ejercicios están llenos de trampas de ese tipo, así que antes de mirar la solución, predice.
Son los ejercicios de redirecciones y tuberías (si no has hecho la práctica, hazla antes). Trabajamos otra vez con accesos.log, el registro del aula 1.3: 16 líneas con fecha, hora, ordenador, usuario, evento (LOGIN/LOGOUT) y resultado (OK/FAIL).
Tabla de Contenidos
Cómo usar este artículo
- Crea una carpeta de pruebas con el
accesos.logde la práctica (las 16 líneas están al principio de la práctica). - Lee el enunciado de cada nivel y la salida esperada, y escribe tus órdenes sin mirar la solución. Si te atascas, lee la pista.
- En «¿Qué falla aquí?», predice qué saldrá antes de leer la explicación. Es donde más se aprende.
- Compara con las soluciones y lee el porqué de cada decisión. Al final tienes el chuletario para repasar en 30 segundos.
Nivel 1 (básico): los intentos fallidos
Guarda en fallos.txt las líneas de accesos.log que son intentos de entrada fallidos y muestra su contenido. Después escribe solo el número de fallos (sin el nombre del fichero detrás).
Salida esperada (el contenido de fallos.txt y, después, el número):
2026-10-19 08:05 pc02 laura LOGIN FAIL 2026-10-19 08:30 pc04 ana LOGIN FAIL 2026-10-19 08:31 pc04 ana LOGIN FAIL 2026-10-19 10:02 pc01 dani LOGIN FAIL 2026-10-19 10:20 pc02 laura LOGIN FAIL 5
grep filtra y > guarda. Para que wc -l no escriba el nombre, no le des el nombre: dale los datos por stdin.
Nivel 2 (intermedio): quién falla más
Saca cuántos intentos fallidos tiene cada persona, de más a menos, y haz que se vean en pantalla y se guarden a la vez en fallos-por-usuario.txt, con una sola tubería. Después comprueba cuántas líneas tiene ese fichero.
Salida esperada:
2 laura
2 ana
1 dani
3 fallos-por-usuario.txt
El patrón de contar es «sacar la columna → ordenar → contar → ordenar por número». Para ver y guardar a la vez hay una orden con forma de T.
Nivel 3 (desafío): el uso de cada ordenador
Crea uso.txt con una cabecera de dos líneas (Uso de los ordenadores del aula 1.3 y Generado por <tu usuario>, con el usuario puesto por bash, no escrito a mano) y debajo cuántos registros tiene cada ordenador. El registro del martes, martes.log, debería leerse también, pero todavía no existe: su error no debe salir en pantalla. Muestra uso.txt y, al final, solo la línea del ordenador más usado.
Salida esperada:
Uso de los ordenadores del aula 1.3
Generado por sergio
4 pc01
5 pc02
3 pc03
4 pc04
5 pc02
(En tu ordenador saldrá tu usuario en lugar de sergio.)
La cabecera, con un here-document (<< EOF, sin comillas para que $USER se sustituya). Los datos, con >>. Para buscar el máximo, sáltate las dos líneas de cabecera con tail -n +3.
¿Qué falla aquí?
Tres fragmentos que parecen correctos. Para cada uno: lee la orden, predice la salida y solo entonces sigue leyendo.
Fragmento 1: contar los fallos de cada persona
# ¿Qué falla aquí? 1: contar los fallos de cada persona grep FAIL accesos.log | cut -d' ' -f4 | uniq -c
¿Qué crees que sale? Esto es lo que salió de verdad:
$ grep FAIL accesos.log | cut -d' ' -f4 | uniq -c
1 laura
2 ana
1 dani
1 laura
laura aparece dos veces, con un 1 cada vez. No es un fallo de uniq: uniq compara cada línea solo con la anterior. Los usuarios de los fallos, en el orden del registro, son laura, ana, ana, dani, laura: las dos ana están pegadas y se juntan, pero las dos laura no. Fíjate en el dibujo:
Usar uniq -c sin un sort delante. Con datos pequeños o ya agrupados parece funcionar, y el día que no lo están te da recuentos partidos sin avisar.
El arreglo es meter sort antes:
# Arreglo: sort antes de uniq, para que los iguales queden juntos grep FAIL accesos.log | cut -d' ' -f4 | sort | uniq -c
Y ahora sí:
$ grep FAIL accesos.log | cut -d' ' -f4 | sort | uniq -c
2 ana
1 dani
2 laura
2 + 1 + 2 = 5 fallos, como en el registro.
Si solo quieres la lista de valores distintos (sin contar), sort -u hace sort | uniq de una vez.
Fragmento 2: guardarlo todo en todo.txt
Usamos el revisar.sh de la práctica: escribe tres líneas correctas por stdout y dos avisos por stderr.
#!/bin/bash # revisar.sh: repasa los cuatro ordenadores del aula (simulado con echo) echo "pc01: correcto" echo "pc02: correcto" echo "AVISO pc03: disco casi lleno" >&2 # >&2: este mensaje sale por stderr echo "pc04: correcto" echo "AVISO pc04: sin red" >&2
Queremos guardar las cinco líneas en todo.txt:
# ¿Qué falla aquí? 2: guardar en todo.txt lo correcto y los avisos bash revisar.sh 2>&1 > todo.txt cat todo.txt
¿Qué sale en pantalla y qué hay en el fichero?
$ bash revisar.sh 2>&1 > todo.txt AVISO pc03: disco casi lleno AVISO pc04: sin red $ cat todo.txt pc01: correcto pc02: correcto pc04: correcto
Los avisos salen en pantalla y todo.txt solo tiene las tres líneas correctas. Las redirecciones se procesan de izquierda a derecha: 2>&1 hace que el 2 apunte a donde apunta el 1 en ese momento, que todavía es la pantalla. Después > todo.txt mueve el 1 al fichero, pero el 2 ya tenía su destino y no lo sigue.
| Paso | Flujo 1 | Flujo 2 |
|---|---|---|
| al empezar | pantalla | pantalla |
2>&1 | pantalla | pantalla (copia del 1) |
> todo.txt | todo.txt | pantalla |
Leer 2>&1 como «el 2 va siempre con el 1». Es una copia del destino actual del 1, no un enlace permanente.
El arreglo es cambiar el orden:
# Arreglo: primero > todo.txt y después 2>&1 bash revisar.sh > todo.txt 2>&1 cat todo.txt
Ahora el 1 se va primero a todo.txt y el 2>&1 copia ese destino:
$ bash revisar.sh > todo.txt 2>&1 $ cat todo.txt pc01: correcto pc02: correcto AVISO pc03: disco casi lleno pc04: correcto AVISO pc04: sin red
Pantalla limpia y las cinco líneas en todo.txt, en el orden en que las escribió el script.
Si te cuesta recordar el orden, usa &> todo.txt: hace lo mismo y no tiene orden que confundir (es de bash).
Fragmento 3: guardar los fallos y contarlos a la vez
# ¿Qué falla aquí? 3: guardar los fallos y contarlos en la misma línea grep FAIL accesos.log > fallos.txt | wc -l cat fallos.txt | wc -l
¿Qué cuenta el primer wc -l?
$ grep FAIL accesos.log > fallos.txt | wc -l 0 $ cat fallos.txt | wc -l 5
Cuenta 0, aunque fallos.txt tiene 5 líneas (el segundo wc lo demuestra). El > fallos.txt se lleva todo el stdout de grep al fichero, así que a la tubería no le queda nada que pasar: wc recibe una entrada vacía. Una salida no puede ir a dos sitios a la vez solo con > y |.
Mezclar > y | en la misma orden esperando que los datos vayan a los dos sitios. La redirección gana y la tubería queda vacía.
El arreglo es tee, que hace exactamente eso: copia al fichero y deja pasar.
# Arreglo: tee guarda la copia y deja pasar los datos hacia wc grep FAIL accesos.log | tee fallos.txt | wc -l
La salida real:
$ grep FAIL accesos.log | tee fallos.txt | wc -l 5
5, y fallos.txt con sus 5 líneas.
Cuando una orden te devuelva «nada» en una tubería, busca un > escondido en mitad de la línea.
Soluciones
Solución del nivel 1
# Nivel 1: los intentos fallidos, guardados y contados grep "LOGIN FAIL" accesos.log > fallos.txt # > crea fallos.txt solo con las líneas que encajan cat fallos.txt wc -l < fallos.txt # < : el número a secas, sin el nombre del fichero
La salida real:
$ grep "LOGIN FAIL" accesos.log > fallos.txt $ cat fallos.txt 2026-10-19 08:05 pc02 laura LOGIN FAIL 2026-10-19 08:30 pc04 ana LOGIN FAIL 2026-10-19 08:31 pc04 ana LOGIN FAIL 2026-10-19 10:02 pc01 dani LOGIN FAIL 2026-10-19 10:20 pc02 laura LOGIN FAIL $ wc -l < fallos.txt 5
Por qué está hecho así:
grep "LOGIN FAIL"con comillas porque el patrón tiene un espacio. Con soloFAILtambién funcionaría aquí (no hay otroFAIL), pero el patrón completo deja claro qué buscas y no se rompe si mañana aparece unLOGOUT FAIL.> fallos.txtporque queremos un fichero nuevo cada vez; con>>, al repetir el ejercicio se duplicarían las líneas.wc -l < fallos.txten lugar dewc -l fallos.txt: con<el fichero lo abre bash ywcno conoce su nombre, así que solo escribe5. La alternativa obvia daría5 fallos.txt.- Cuenta a mano: laura a las 08:05 y 10:20, ana a las 08:30 y 08:31, dani a las 10:02: 2 + 2 + 1 = 5.
Solución del nivel 2
# Nivel 2: quién falla más, guardado y en pantalla a la vez grep "LOGIN FAIL" accesos.log | cut -d' ' -f4 | sort | uniq -c | sort -nr | tee fallos-por-usuario.txt wc -l fallos-por-usuario.txt # una línea por persona que ha fallado
La salida real:
$ grep "LOGIN FAIL" accesos.log | cut -d' ' -f4 | sort | uniq -c | sort -nr | tee fallos-por-usuario.txt
2 laura
2 ana
1 dani
$ wc -l fallos-por-usuario.txt
3 fallos-por-usuario.txt
Por qué está hecho así: la tubería tiene cinco etapas, y cada una deja los datos listos para la siguiente:
| Etapa | Qué sale | Líneas |
|---|---|---|
grep "LOGIN FAIL" | las 5 líneas de fallos | 5 |
cut -d' ' -f4 | laura, ana, ana, dani, laura | 5 |
sort | ana, ana, dani, laura, laura | 5 |
uniq -c | 2 ana, 1 dani, 2 laura | 3 |
sort -nr | 2 laura, 2 ana, 1 dani | 3 |
tee fallos-por-usuario.txt | lo mismo, en pantalla y en el fichero | 3 |
- Sin el
sortde la tercera etapa, laura saldría partida (es el fragmento 1). sort -nry nosort -r: aquí todos los números tienen una cifra, pero con un 10 el orden de texto fallaría.teeva al final porque queremos guardar el ranking completo; si hubiera unheaddetrás, tendría que ir antes que él.- laura sale antes que ana porque empatan a 2 y
sort -rdesempata con el resto de la línea al revés.
Solución del nivel 3
# Nivel 3: informe de uso de cada ordenador, con cabecera y sin ruido de errores cat << EOF > uso.txt Uso de los ordenadores del aula 1.3 Generado por $USER EOF cat accesos.log martes.log 2> /dev/null | cut -d' ' -f3 | sort | uniq -c >> uso.txt # martes.log aún no existe cat uso.txt tail -n +3 uso.txt | sort -nr | head -1 # desde la línea 3 (sin cabecera): el más usado
La salida real:
$ cat << EOF > uso.txt
> Uso de los ordenadores del aula 1.3
> Generado por $USER
> EOF
$ cat accesos.log martes.log 2> /dev/null | cut -d' ' -f3 | sort | uniq -c >> uso.txt
$ cat uso.txt
Uso de los ordenadores del aula 1.3
Generado por sergio
4 pc01
5 pc02
3 pc03
4 pc04
$ tail -n +3 uso.txt | sort -nr | head -1
5 pc02
Por qué está hecho así:
- La cabecera con
<< EOFy>. El here-document le da dos líneas acaty>creauso.txtcon ellas. Sin comillas enEOF, bash cambia$USERpor tu usuario (sergioen mi ejecución). Es la única orden con>: todo lo demás añade. cat accesos.log martes.log 2> /dev/null.catlee los dos ficheros seguidos; comomartes.logno existe, se queja por stderr, y2> /dev/nullse traga la queja. Lo que sí pudo leer (las 16 líneas del lunes) sigue por la tubería. El día que existamartes.log, la misma orden lo sumará sin cambiar nada.cut -d' ' -f3 | sort | uniq -c >> uso.txt. El patrón de contar, por ordenador, añadido debajo de la cabecera. Cuenta a mano: pc01 4 + pc02 5 + pc03 3 + pc04 4 = 16.tail -n +3 uso.txt | sort -nr | head -1.tail -n +3empieza en la línea 3, saltándose la cabecera (con+, «desde la línea N»; sin+, «las N últimas»). Después ordenamos por número y nos quedamos con la primera: pc02.
Si no te saltas la cabecera, sort -nr trata las líneas sin número como un 0 y las deja al final, así que aquí también funcionaría. Pero depender de eso es frágil: con tail -n +3 dices exactamente lo que quieres.
Pruébalo en tu terminal
El fragmento 1 en pequeño, para verlo con tus propios ojos sin necesidad del registro. Teclea estas órdenes y compara:
$ printf 'laura\nana\nana\nlaura\n' | uniq -c
1 laura
2 ana
1 laura
$ printf 'laura\nana\nana\nlaura\n' | sort | uniq -c
2 ana
2 laura
$ echo hola > x.txt | wc -l
0
Qué observar:
- Sin
sort,uniq -cda tres grupos:laura,ana anaylauraotra vez, porque las doslaurano están pegadas. - Con
sort, dos grupos:2 anay2 laura. Mismos datos, distinto orden, distinto resultado. - En la última orden,
wc -lcuenta 0: el> x.txtse ha llevado elholaal fichero (compruébalo concat x.txt). Es el fragmento 3. - Cambia la última por
echo hola | tee x.txt | wc -l: ahora cuenta 1 yx.txttambién tienehola.
Chuletario: redirecciones y tuberías
Lo esencial en 30 segundos
- Hay tres flujos: 0 stdin, 1 stdout y 2 stderr. Por defecto, el 0 es el teclado y el 1 y el 2, la pantalla.
>vacía y escribe;>>añade. Nuncaorden f > f.2>&1copia el destino actual del 1: primero> f, después2>&1.- Por
|solo viaja stdout. Para meter los errores:2>&1 |. - Para contar:
sort | uniq -c | sort -nr. Para guardar y seguir:tee.
Redirecciones
| Quiero… | Código | Resultado |
|---|---|---|
| guardar la salida (desde cero) | orden > f | f solo tiene la salida nueva |
| añadir al final | orden >> f | f crece |
| leer un fichero por stdin | orden < f | la orden no ve el nombre (wc -l < f → 16) |
| guardar solo los errores | orden 2> err.txt | la salida normal sigue en pantalla |
| guardarlo todo | orden > f 2>&1 o orden &> f | salida y errores, en orden |
| tirar los errores | orden 2> /dev/null | pantalla limpia; $? sigue avisando |
| escribir un aviso en stderr | echo "AVISO" >&2 | se puede separar con 2> |
| texto de entrada en la orden | cat << EOF > f … EOF | $USER se sustituye (con 'EOF', no) |
Tuberías y filtros
| Quiero… | Código | Resultado con accesos.log |
|---|---|---|
| líneas que contienen algo | grep "LOGIN FAIL" f | 5 líneas |
| cuántas | grep -c "LOGIN FAIL" f | 5 |
| una columna | cut -d' ' -f4 f | los usuarios |
| valores distintos | cut -d' ' -f3 f | sort -u | pc01 … pc04 |
| contar repeticiones | cut -d' ' -f4 f | sort | uniq -c | sort -nr | 5 laura primero |
| las N primeras / últimas | head -n N / tail -n N | |
| desde la línea N | tail -n +N | sin cabecera |
| guardar y seguir | … | tee f | … | f con todo lo que pasó |
| errores también en la tubería | orden 2>&1 | filtro |
Plantillas de los ejercicios
# Nivel 1: filtrar, guardar y contar sin nombre grep "LOGIN FAIL" accesos.log > fallos.txt wc -l < fallos.txt # 5 # Nivel 2: ranking visto y guardado grep "LOGIN FAIL" accesos.log | cut -d' ' -f4 | sort | uniq -c | sort -nr | tee fallos-por-usuario.txt # Nivel 3: cabecera + datos + máximo cat << EOF > uso.txt Uso de los ordenadores del aula 1.3 Generado por $USER EOF cat accesos.log martes.log 2> /dev/null | cut -d' ' -f3 | sort | uniq -c >> uso.txt tail -n +3 uso.txt | sort -nr | head -1 # 5 pc02
Síntoma → causa probable
| Síntoma | Causa probable |
|---|---|
| El fichero se ha quedado vacío (0 bytes) | Leíste y escribiste el mismo fichero con > |
| Mi informe solo tiene el último dato | Usaste > en vez de >> |
| El error sigue saliendo en pantalla | Solo redirigiste el 1 (>), o escribiste 2>&1 > f |
grep -c cuenta 0 errores con la pantalla llena | Los errores van por stderr: falta 2>&1 antes de | |
uniq -c repite un valor con recuentos partidos | Falta sort antes de uniq |
| «100» sale antes que «9» | sort sin -n |
wc de una tubería da 0 | Hay un > en mitad de la tubería: usa tee |
wc -l escribe el nombre del fichero | Usa wc -l < f |
Antes de entregar, pregúntate
- ¿Hay algún
>que vaya a pisar un fichero que necesito, o el mismo que estoy leyendo? - En cada orden, ¿a dónde va el flujo 1 y a dónde el 2?
- Si uso
uniq, ¿tengo unsortdelante? - Si silencio errores con
/dev/null, ¿de verdad no me importan?
Con esto terminamos redirecciones y tuberías. Lo siguiente en Linux son los shell scripts: guardar todas estas órdenes en un fichero y ejecutarlas de una vez.
Sigue aprendiendoTodos los artículos de Linux¿Te ha servido este artículo? Así puedes apoyar SergioLearns →




Qué es un sistema operativo — teoría con tu portátil en una tarde de estudio, una llamada a write() y la CPU que nunca descansa (y los tres programas que no se ejecutaban a la vez)