Redirecciones y tuberías en Linux — práctica con un informe que crece con >>, un ranking guardado con tee y avisos separados de lo correcto (y el informe que se comió a sí mismo)
La primera vez que intenté sacar un informe del registro del aula lo hice «a mano»: una orden por dato, cada una con su > hacia informe.txt. Registros, entradas correctas, fallos… Diez órdenes. Hice cat informe.txt para admirar mi obra y solo había una cosa: la lista de usuarios, que era lo último que había escrito. Cada > había vaciado el fichero antes de escribir, así que cada dato se había comido al anterior. La lección, que vas a ver aplicada en los tres programas: > una sola vez, al principio; >> para todo lo demás. Y si algo tiene que ir a dos sitios, no lo repitas: tee.
Esta es la práctica de la teoría de redirecciones y tuberías. Son tres «programas» de terminal, de nivel medio, que trabajan sobre el mismo accesos.log del aula 1.3 (16 registros de entradas y salidas de un lunes). Los datos se parecen a los de verdad a propósito: hay contraseñas mal puestas, gente que entra dos veces, un ordenador que da avisos… El primero construye un informe con > y >>; el segundo saca rankings con tuberías y guarda una copia con tee; el tercero separa lo correcto de los avisos con 2>, 2>&1 y /dev/null.
Tabla de Contenidos
Antes de empezar
Conviene tener claro:
- Qué son stdout (1) y stderr (2), y que por defecto los dos van a la pantalla.
- La diferencia entre
>(vacía y escribe) y>>(añade al final). - Que
|conecta el stdout de una orden con el stdin de la siguiente, y queuniqsolo junta repetidos seguidos. - Cómo se corta una columna con
cut -d' ' -fN.
Si alguno te suena dudoso, repasa la teoría antes de seguir.
Para seguir la práctica, crea una carpeta de pruebas y dentro un fichero accesos.log con estas 16 líneas (pégalas en un editor, o usa un here-document como en la teoría: cat << EOF > accesos.log, las líneas y EOF).
2026-10-19 08:02 pc01 sergio LOGIN OK 2026-10-19 08:05 pc02 laura LOGIN FAIL 2026-10-19 08:06 pc02 laura LOGIN OK 2026-10-19 08:11 pc03 pablo LOGIN OK 2026-10-19 08:30 pc04 ana LOGIN FAIL 2026-10-19 08:31 pc04 ana LOGIN FAIL 2026-10-19 08:33 pc04 ana LOGIN OK 2026-10-19 09:58 pc01 sergio LOGOUT OK 2026-10-19 10:02 pc01 dani LOGIN FAIL 2026-10-19 10:03 pc01 dani LOGIN OK 2026-10-19 10:15 pc02 laura LOGOUT OK 2026-10-19 10:20 pc02 laura LOGIN FAIL 2026-10-19 10:21 pc02 laura LOGIN OK 2026-10-19 11:40 pc03 pablo LOGOUT OK 2026-10-19 11:45 pc03 sergio LOGIN OK 2026-10-19 12:00 pc04 ana LOGOUT OK
Las salidas que verás son reales: se ejecutaron una vez como el usuario sergio con la terminal en inglés (LANG=C). Si la tuya está en español, solo cambiarán los mensajes de error.
Programa 1: el informe del lunes con > y >>
Queremos un fichero informe.txt con un resumen del día: cuántos registros hay, cuántas entradas fueron bien, cuántos intentos fallaron, cuántas salidas, y la lista de usuarios distintos. Todo con órdenes que ya conocemos, pero cada una escribe su trozo en el mismo fichero. Lo que lo complica: que los números salgan al lado de su etiqueta (Registros: 16, no Registros: y el 16 en otra línea) y que nada se pise.
La idea
Un informe es un fichero que crece. El plan:
- La primera línea, el título, con
>: así cada vez que lo ejecutes empiezas con un informe limpio y no se acumulan los de días anteriores. - Cada dato en dos órdenes: un
echo -n "Etiqueta: " >>(la-nevita el salto de línea) y la orden que calcula el número, también con>>. Como elechono terminó la línea, el número cae justo al lado. - Para que
wc -lno escriba el nombre del fichero,< accesos.log.grep -cya da el número solo. - La lista de usuarios con la tubería
cut | sort -u, redirigida al final con>>.
Antes de programar, piensa en qué orden crea el fichero. Si hay más de un >, algo se va a perder.
El código
# Programa 1: el informe del lunes, línea a línea con > y >> echo "== Aula 1.3: lunes 19 de octubre ==" > informe.txt # > solo la primera vez: informe limpio echo -n "Registros: " >> informe.txt # -n: sin salto de línea, el número irá al lado wc -l < accesos.log >> informe.txt # < para que wc no escriba el nombre del fichero echo -n "Entradas correctas: " >> informe.txt grep -c "LOGIN OK" accesos.log >> informe.txt # comillas: el patrón lleva un espacio echo -n "Intentos fallidos: " >> informe.txt grep -c "LOGIN FAIL" accesos.log >> informe.txt echo -n "Salidas: " >> informe.txt grep -c LOGOUT accesos.log >> informe.txt echo "Usuarios distintos:" >> informe.txt cut -d' ' -f4 accesos.log | sort -u >> informe.txt # la redirección va al final de la tubería cat informe.txt wc -l informe.txt
Salida
$ echo "== Aula 1.3: lunes 19 de octubre ==" > informe.txt $ echo -n "Registros: " >> informe.txt $ wc -l < accesos.log >> informe.txt $ echo -n "Entradas correctas: " >> informe.txt $ grep -c "LOGIN OK" accesos.log >> informe.txt $ echo -n "Intentos fallidos: " >> informe.txt $ grep -c "LOGIN FAIL" accesos.log >> informe.txt $ echo -n "Salidas: " >> informe.txt $ grep -c LOGOUT accesos.log >> informe.txt $ echo "Usuarios distintos:" >> informe.txt $ cut -d' ' -f4 accesos.log | sort -u >> informe.txt $ cat informe.txt == Aula 1.3: lunes 19 de octubre == Registros: 16 Entradas correctas: 7 Intentos fallidos: 5 Salidas: 4 Usuarios distintos: ana dani laura pablo sergio $ wc -l informe.txt 11 informe.txt
Comprobemos las cuentas a mano. El registro tiene 16 líneas, y cada una es una entrada correcta (LOGIN OK), un fallo (LOGIN FAIL) o una salida (LOGOUT): 7 + 5 + 4 = 16. Los usuarios distintos son cinco y salen en orden alfabético porque sort -u ordena y quita repetidos a la vez. Y wc -l informe.txt dice 11: 1 título + 4 líneas de datos + 1 etiqueta + 5 usuarios = 11.
El dibujo muestra qué orden escribió cada línea del fichero:
Fíjate en las líneas 2 a 5: cada una la escriben dos órdenes. El echo -n deja la etiqueta sin salto de línea y el grep -c (o el wc -l) termina la línea con su número y su salto.
Vamos a hacer zoom a la línea más curiosa, la de los registros:
wc -l < accesos.log >> informe.txt # < para que wc no escriba el nombre del fichero
Aquí hay dos redirecciones en la misma orden: < conecta accesos.log a la entrada de wc y >> conecta su salida al informe. Bash prepara las dos antes de arrancar wc, que solo ve «datos que entran» y «un sitio donde escribir». Sin el < (con wc -l accesos.log), la línea quedaría Registros: 16 accesos.log.
| Paso | Orden | Contenido de informe.txt después |
|---|---|---|
| 1 | echo "== Aula… ==" > informe.txt | 1 línea (el título) |
| 2 | echo -n "Registros: " >> | título + Registros: (línea a medias) |
| 3 | wc -l < accesos.log >> | título + Registros: 16 |
| 4-9 | tres parejas echo -n + grep -c | 5 líneas |
| 10 | echo "Usuarios distintos:" >> | 6 líneas |
| 11 | cut … | sort -u >> | 11 líneas |
Preguntas que te puedes hacer
¿Por qué el >> va al final de la tubería y no después de cut? Porque la redirección afecta a la orden en la que está escrita. cut … >> informe.txt | sort -u mandaría la salida de cut al fichero (sin ordenar ni quitar repetidos) y a sort no le llegaría nada. En una tubería, lo que va al fichero es lo que sale de la última orden.
¿Por qué "LOGIN OK" va entre comillas y LOGOUT no? Porque LOGIN OK tiene un espacio. Sin comillas, grep -c LOGIN OK accesos.log entendería que buscas LOGIN en dos ficheros llamados OK y accesos.log, y se quejaría de que OK no existe.
¿Y si buscara solo LOGIN? Contaría 12: las 7 entradas correctas y los 5 fallos, porque las dos contienen LOGIN. Hay que buscar el patrón completo.
¿Por qué no grep -c OK para las correctas? Porque los LOGOUT OK también terminan en OK: saldrían 11 (7 + 4). Elegir bien el patrón es la mitad del trabajo con grep.
¿Qué pasa si lo ejecuto dos veces? Que obtienes el mismo informe, gracias a que la primera orden usa >. Si también fuera >>, la segunda ejecución añadiría otro informe completo debajo del primero.
Poner > en todas las líneas (o en la que no es la primera). Cada > vacía el fichero y te quedas solo con el último dato, que es justo lo que me pasó a mí.
Si alguna vez dudas de si un fichero se está pisando, wc -l fichero después de cada orden te lo dice en un segundo.
Pruébalo tú
- Añade una línea
Ordenadores usados:con el número de ordenadores distintos (pista:cut -d' ' -f3 accesos.log | sort -u | wc -l; recuerda el-ndelecho). - Cambia el programa para que el informe del martes se añada debajo del del lunes en un
semana.txt, sin perder el del lunes.
Programa 2: ranking de usuarios, fallos y hora punta
Ahora queremos estadísticas: quién usa más el aula (y guardar el ranking completo en ranking.txt aunque en pantalla solo veamos el top 3), quién se equivoca más con la contraseña y a qué hora hay más movimiento. Lo que lo complica: no hay ninguna orden que «cuente por usuario»; hay que construirlo con piezas.
La idea
El patrón que vamos a repetir tres veces es sacar → ordenar → contar → ordenar por el número:
cutsaca la columna que interesa (usuario o hora).sortjunta los iguales.uniq -ccuenta cada grupo.sort -nrordena por ese número, de mayor a menor.
Y para guardar el ranking entero y mostrar solo el principio, tee va antes de head: así el fichero recibe todo y head corta solo lo que va a la pantalla.
Antes de programar, piensa en dónde tiene que ir tee. Lo que llega a tee es lo que acaba en el fichero.
El código
# Programa 2: ranking de usuarios, fallos por persona y hora punta cut -d' ' -f4 accesos.log | sort | uniq -c | sort -nr | tee ranking.txt | head -3 wc -l ranking.txt # tee guardó el ranking ENTERO, no solo el top 3 grep "LOGIN FAIL" accesos.log | cut -d' ' -f4 | sort | uniq -c | sort -nr cut -d' ' -f2 accesos.log | cut -d: -f1 | sort | uniq -c | sort -nr | head -1
Salida
$ cut -d' ' -f4 accesos.log | sort | uniq -c | sort -nr | tee ranking.txt | head -3
5 laura
4 ana
3 sergio
$ wc -l ranking.txt
5 ranking.txt
$ grep "LOGIN FAIL" accesos.log | cut -d' ' -f4 | sort | uniq -c | sort -nr
2 laura
2 ana
1 dani
$ cut -d' ' -f2 accesos.log | cut -d: -f1 | sort | uniq -c | sort -nr | head -1
7 08
Comprobación a mano. En el ranking, laura tiene 5 registros, ana 4, sergio 3, y pablo y dani 2 cada uno: 5 + 4 + 3 + 2 + 2 = 16. En pantalla solo salen tres porque head -3 corta, pero wc -l ranking.txt dice 5: el fichero tiene el ranking completo. En los fallos, 2 + 2 + 1 = 5, los mismos 5 intentos fallidos del programa 1. Y la hora punta son las 8: hay 7 registros entre las 08:02 y las 08:33.
El dibujo muestra por qué el fichero y la pantalla no tienen lo mismo:
Vamos al zoom de la última tubería, la de la hora punta, que tiene dos cut seguidos:
cut -d' ' -f2 accesos.log | cut -d: -f1 | sort | uniq -c | sort -nr | head -1
El primer cut -d' ' -f2 se queda con la hora (08:02). El segundo cambia de delimitador, -d:, y se queda con lo que hay antes de los dos puntos (08). Así agrupamos por hora en punto, no por minuto. Esta es la traza, etapa a etapa:
| Etapa | Qué sale (resumido) | Líneas |
|---|---|---|
cut -d' ' -f2 | 08:02, 08:05, 08:06, … 12:00 | 16 |
cut -d: -f1 | 08, 08, 08, … 12 | 16 |
sort | uniq -c | 7 08, 1 09, 5 10, 2 11, 1 12 | 5 |
sort -nr | 7 08, 5 10, 2 11, … | 5 |
head -1 | 7 08 | 1 |
7 + 1 + 5 + 2 + 1 = 16.
Preguntas que te puedes hacer
¿Por qué laura sale antes que ana en los fallos, si las dos tienen 2? Porque cuando dos números empatan, sort desempata comparando el resto de la línea, y con -r también lo hace al revés: «laura» va después de «ana» en el alfabeto, así que con -r va antes. Si quieres que el empate salga en orden alfabético normal, sort -k1,1nr -k2 ordena por la primera columna como número descendente y, a igualdad, por la segunda.
¿Por qué sort -n y no solo sort? Porque uniq -c deja los números con espacios delante y, aunque aquí todos tienen una cifra, el día que haya un 10 el sort de texto lo pondría antes que el 9. Con números, siempre -n.
¿Qué pasa si pongo tee después de head? Que ranking.txt solo tendría las tres primeras líneas. tee guarda lo que le llega, y después de head solo llegan tres.
¿Por qué en los fallos hay sort antes de uniq -c si ya están «casi» juntos? Porque no lo están: laura falló a las 08:05 y a las 10:20, con otros fallos entre medias. Sin sort, laura saldría dos veces con un 1 (lo verás en los ejercicios).
¿head -1 y head -n 1 son lo mismo? Sí. -n 1 es la forma «oficial» y -1 el atajo de siempre.
Poner tee al final pensando que guarda todo el ranking. Guarda lo que le llega, y al final solo llega el recorte.
uniq -c alinea los números con espacios a la izquierda ( 5 laura). Es normal: así las columnas quedan ordenadas en pantalla.
Pruébalo tú
- Haz el ranking de ordenadores (columna 3) y guárdalo en
pcs.txt, mostrando solo el más usado. - Saca cuántos registros hubo por la mañana (antes de las 10): pista,
cutde la hora ygrep -cde las que empiezan por08o09(grep -c '^0[89]').
Programa 3: separar lo correcto de los avisos
El técnico del aula tiene un pequeño script, revisar.sh, que repasa los cuatro ordenadores. Escribe una línea por ordenador: si todo va bien, por stdout; si hay un problema, un AVISO por stderr. Queremos guardar lo correcto en un fichero, los avisos en otro, contar los avisos y, al final, tenerlo todo junto y en orden. Lo que lo complica: en pantalla las cinco líneas parecen iguales.
La idea
Primero hay que ver el script por dentro, para saber qué sale por cada flujo:
#!/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
El truco está en >&2: es la redirección «manda esta salida al flujo 2», es decir, echo escribe en stderr en vez de en stdout. Así es como los programas (y tus scripts) escriben sus errores. Con eso claro, el plan es:
- Ejecutarlo tal cual para ver la mezcla.
- Separar con
>(flujo 1) y2>(flujo 2), cada uno a su fichero. - Para contar los avisos con
grep -c, tienen que entrar en la tubería:2>&1 |. - Para quedarnos solo con los correctos, los avisos a
/dev/null. - Para tenerlo todo junto en un fichero,
> todo.txt 2>&1, en ese orden.
Antes de programar, piensa en cada orden en términos de «¿a dónde va el 1?, ¿a dónde va el 2?». Es la única pregunta que importa.
El código
# Programa 3: separar lo correcto de los avisos de revisar.sh bash revisar.sh # en pantalla se mezclan stdout y stderr bash revisar.sh > correctos.txt # los avisos (stderr) siguen saliendo bash revisar.sh > correctos.txt 2> avisos.txt # cada flujo a su fichero: pantalla limpia cat correctos.txt cat avisos.txt bash revisar.sh 2>&1 | grep -c AVISO # para filtrar avisos, tienen que entrar en la tubería bash revisar.sh 2> /dev/null | wc -l # solo los correctos: los avisos, a la basura bash revisar.sh > todo.txt 2>&1 # todo junto, en su orden, en un solo fichero cat todo.txt
Salida
$ bash revisar.sh pc01: correcto pc02: correcto AVISO pc03: disco casi lleno pc04: correcto AVISO pc04: sin red $ bash revisar.sh > correctos.txt AVISO pc03: disco casi lleno AVISO pc04: sin red $ bash revisar.sh > correctos.txt 2> avisos.txt $ cat correctos.txt pc01: correcto pc02: correcto pc04: correcto $ cat avisos.txt AVISO pc03: disco casi lleno AVISO pc04: sin red $ bash revisar.sh 2>&1 | grep -c AVISO 2 $ bash revisar.sh 2> /dev/null | wc -l 3 $ 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
Repasemos. En la primera ejecución salen las cinco líneas mezcladas. En la segunda, > correctos.txt se lleva las tres correctas y en pantalla quedan los dos avisos, la prueba de que iban por stderr. En la tercera la pantalla queda limpia: cada flujo tiene su fichero. Los cat lo confirman: 3 líneas en correctos.txt y 2 en avisos.txt (3 + 2 = 5, las cinco que escribe el script).
grep -c AVISO da 2 porque los avisos entraron en la tubería gracias al 2>&1. wc -l con 2> /dev/null da 3: solo los correctos. Y todo.txt tiene las cinco líneas en el mismo orden en que el script las escribió.
Zoom a la orden que más se confunde:
bash revisar.sh 2>&1 | grep -c AVISO # para filtrar avisos, tienen que entrar en la tubería
Aquí no hay fichero, pero el orden sigue importando: bash conecta primero la tubería al flujo 1 y luego procesa 2>&1, que hace que el 2 apunte «a donde apunta el 1», es decir, a la tubería. Por eso los dos flujos llegan a grep.
| Orden | El 1 va a | El 2 va a | En pantalla |
|---|---|---|---|
bash revisar.sh | pantalla | pantalla | 5 líneas |
… > correctos.txt | correctos.txt | pantalla | 2 avisos |
… > correctos.txt 2> avisos.txt | correctos.txt | avisos.txt | nada |
… 2>&1 | grep -c AVISO | tubería | tubería | 2 |
… 2> /dev/null | wc -l | tubería | /dev/null | 3 |
… > todo.txt 2>&1 | todo.txt | todo.txt | nada |
Preguntas que te puedes hacer
¿Por qué bash revisar.sh y no ./revisar.sh? Porque el fichero no tiene permiso de ejecución (644, como vimos en Permisos en Linux). Con bash revisar.sh es bash quien lo lee y lo ejecuta, y solo hace falta permiso de lectura. Con chmod u+x revisar.sh podrías usar ./revisar.sh.
¿Por qué en todo.txt las líneas salen en orden y no «primero las buenas y luego los avisos»? Porque los dos flujos apuntan al mismo fichero abierto y cada echo escribe en el momento. Si redirigieras a dos ficheros y luego los juntaras con cat, perderías el orden.
¿Y si escribo 2> todo.txt > todo.txt? Que bash abre el fichero dos veces, por separado, y cada flujo escribe en su propia posición: las líneas se pisan entre ellas. Para «los dos al mismo fichero», siempre > todo.txt 2>&1 (o &> todo.txt).
¿/dev/null borra los avisos del script? No: el script los sigue escribiendo, solo que van a un sitio que los descarta. Si el problema es real (un disco casi lleno), sigue ahí.
¿Qué hace >&2 exactamente? Es 1>&2: «el flujo 1 de este echo apunta a donde apunte el 2″. Es lo mismo que 2>&1, pero al revés.
Intentar contar errores con orden | grep -c error sin 2>&1. Los errores van por stderr, no entran en la tubería y el recuento sale 0 (mientras los mensajes aparecen en pantalla).
En tus scripts, escribe los avisos con echo "…" >&2. Así quien los use podrá separar lo correcto de los avisos exactamente como hemos hecho aquí.
Pruébalo tú
- Guarda solo los avisos en
avisos.txtsin que salga nada en pantalla y sin crearcorrectos.txt(pista: el 1 a/dev/null). - Añade los avisos de cada ejecución a un histórico
avisos-semana.txten vez de pisarlo.
Errores típicos en esta práctica
>en todas las líneas de un informe. Cada una vacía el fichero: solo queda la última.- La redirección en mitad de la tubería.
cut … > f | sortmanda aflo decuty deja asortsin datos. - Patrones de
grepdemasiado cortos.OKtambién encuentra losLOGOUT OK; busca"LOGIN OK"con comillas. uniq -csinsortdelante. Los recuentos salen partidos cuando los iguales no están seguidos.teedespués dehead. El fichero se queda solo con el recorte.- Contar errores sin
2>&1. stderr no entra en la tubería.
Pruébalo en tu terminal
No hace falta revisar.sh para ver los dos flujos: { …; } agrupa varias órdenes y bash trata el grupo como un solo programa, así que puedes redirigir su stdout y su stderr. Teclea esto y compara con la transcripción (fíjate en el espacio después de { y en el ; antes de }, son obligatorios):
$ { echo "pc01: correcto"; echo "AVISO pc03" >&2; } > bien.txt 2> mal.txt
$ cat bien.txt
pc01: correcto
$ cat mal.txt
AVISO pc03
$ { echo "pc01: correcto"; echo "AVISO pc03" >&2; } | wc -l
AVISO pc03
1
$ { echo "pc01: correcto"; echo "AVISO pc03" >&2; } 2>&1 | wc -l
2
Qué observar:
- La primera orden no escribe nada en pantalla: el
echonormal va abien.txty el que lleva>&2va amal.txt. - Los
catconfirman que cada línea acabó en su fichero. - En la penúltima,
| wc -lcuenta 1, y elAVISO pc03aparece en pantalla: no entró en la tubería. - En la última, con
2>&1delante de la barra,wc -lcuenta 2. - Cambia
> bien.txt 2> mal.txtpor&> todo.txty mira qué queda dentro.
Resumen
- Un informe que crece:
>una sola vez, al principio;>>en todas las demás órdenes. echo -n+ una orden que da un número (wc -l < f,grep -c) escriben «etiqueta: número» en la misma línea.- El patrón de contar:
cut | sort | uniq -c | sort -nr, yheadpara quedarte con los primeros. teeguarda lo que le llega y deja seguir la tubería: ponlo antes del recorte.> f 2> gsepara los flujos;2>&1 |mete los errores en la tubería;2> /dev/nulllos descarta;> f 2>&1lo guarda todo en orden.- Los scripts escriben sus avisos con
>&2para que se puedan separar.
Checklist: ¿lo has practicado?
- ¿Has ejecutado los tres programas y comprobado las salidas?
- ¿Sabes explicar por qué cada línea del informe del programa 1 la escribió la orden que la escribió?
- ¿Puedes decir, sin mirar, qué tiene
ranking.txty qué sale en pantalla en el programa 2? - ¿Sabes a dónde van el flujo 1 y el 2 en cada orden del programa 3?
- ¿Has intentado los «Pruébalo tú» sin mirar la teoría?
Si has respondido que sí a casi todo, estás listo para los ejercicios de redirecciones y tuberías, donde ya no te doy las órdenes hechas. ¡Nos vemos allí!
Siguiente paso: ejerciciosRedirecciones 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)¿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)