Redirecciones y tuberías en Linux — teoría con el registro del aula, el misterioso orden de 2>&1 y filtros que se encadenan (y un sort que me dejó sin la lista del grupo)

Era la semana de entregar la práctica de IC2 y yo tenía un fichero usuarios.txt con los nombres del grupo, desordenados. Pensé: «lo ordeno y lo guardo en el mismo sitio, una línea y listo». Escribí esto, le di a Intro y me fui a por un café:

# El gancho: quería dejar ordenada la lista del grupo "en el mismo fichero"
cat usuarios.txt                  # cinco nombres, sin orden
sort usuarios.txt > usuarios.txt  # parece lógico: ordenar y guardar encima
cat usuarios.txt                  # ...y no sale nada
wc -c usuarios.txt                # 0 bytes: el fichero se ha vaciado

Cuando volví, esto es lo que había en la terminal:

$ cat usuarios.txt
pablo
laura
sergio
ana
dani
$ sort usuarios.txt > usuarios.txt
$ cat usuarios.txt
$ wc -c usuarios.txt
0 usuarios.txt

Ni un error, ni un aviso. Simplemente el fichero estaba vacío: 0 bytes. La lista del grupo había desaparecido y tuve que volver a escribirla de memoria. ¿Qué pasó? Que el > no espera a que sort termine: bash vacía usuarios.txt antes de arrancar sort, así que sort lee un fichero que ya está vacío y no tiene nada que ordenar. Al final de este artículo vas a entender exactamente por qué, y cómo hacerlo bien.

Hoy toca el shell avanzado: cómo mandar la salida de una orden a un fichero (>, >>), cómo leer de un fichero (<), qué hacer con los mensajes de error (2>, 2>&1, /dev/null) y cómo encadenar órdenes con tuberías (|) para que la salida de una sea la entrada de la siguiente. Con eso, los filtros de siempre (grep, sort, uniq, wc, head, tail, cut y tee) se convierten en una navaja suiza. Si vienes de Permisos en Linux, ya sabes leer y cambiar quién puede tocar un fichero; ahora vamos a ver cómo llenarlos. Después tienes la práctica con tres programas completos.

El registro del aula que nos acompaña

Todos los ejemplos de este tema usan el mismo fichero: accesos.log, el registro de entradas y salidas del aula 1.3 de un lunes cualquiera. Tiene 16 líneas como estas:

# stdout y stderr: dos salidas distintas que por defecto van a la misma pantalla
head -3 accesos.log                   # stdout: las tres primeras líneas del registro
ls accesos.log noexiste               # una parte va bien (stdout) y otra mal (stderr)
ls accesos.log noexiste > salida.txt  # > solo desvía stdout: el error sigue en pantalla
cat salida.txt                        # en el fichero solo está lo que fue bien

De momento fíjate solo en la primera orden, head -3 accesos.log (las tres primeras líneas). Cada línea tiene seis columnas separadas por un espacio:

ColumnaQué esEjemplo
1fecha2026-10-19
2hora08:05
3ordenadorpc02
4usuariolaura
5eventoLOGIN o LOGOUT
6resultadoOK o FAIL (contraseña mal puesta)

Esto importa porque herramientas como cut trabajan por columnas: «dame la columna 4» será «dame el usuario». Guárdate la tabla, la usaremos todo el rato.

Todas las salidas de este artículo son reales: las he ejecutado una vez en un Linux con el usuario sergio y el idioma en inglés (LANG=C). Si tu terminal está en español, los mensajes de error saldrán traducidos (por ejemplo, «No existe el fichero o el directorio»), pero todo lo demás será igual.

Tres flujos: stdin, stdout y stderr

Antes de redirigir nada hay que saber qué se redirige. Todo programa de Linux nace con tres «tuberías» ya conectadas, numeradas:

NúmeroNombrePara qué sirveA dónde va por defecto
0stdin (entrada estándar)de donde el programa leeel teclado
1stdout (salida estándar)los resultados normalesla pantalla
2stderr (salida de error)los mensajes de error y avisosla pantalla

A esos números se les llama descriptores de fichero. ¿Te suenan? Son los mismos que usábamos con read y write en las llamadas al sistema en C: write(1, …) escribía en pantalla y write(2, …) también, pero por el canal de errores.

La trampa es que stdout y stderr acaban en la misma pantalla, así que a simple vista no los distingues. Mira la salida completa de la sesión de antes:

$ head -3 accesos.log
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
$ ls accesos.log noexiste
ls: cannot access 'noexiste': No such file or directory
accesos.log
$ ls accesos.log noexiste > salida.txt
ls: cannot access 'noexiste': No such file or directory
$ cat salida.txt
accesos.log

Vamos por partes:

  • ls accesos.log noexiste pide dos cosas. El fichero accesos.log existe, así que ls escribe su nombre por stdout. noexiste no existe, así que ls escribe una queja por stderr. En pantalla salen las dos líneas, una debajo de otra, como si fueran lo mismo.
  • ls accesos.log noexiste > salida.txt manda stdout al fichero. Pero el error sigue saliendo en pantalla: > solo toca el flujo 1.
  • cat salida.txt lo confirma: dentro solo está accesos.log, lo que fue bien.

Fíjate en el dibujo: el programa no sabe ni le importa a dónde van sus flujos. Quien los conecta es bash, antes de arrancarlo.

Los tres flujos de un programaTodo programa tiene tres flujos: 0 stdin, que lee del teclado, 1 stdout y 2 stderr, que escriben en la pantalla. A la derecha, con > salida.txt solo cambia el destino de stdout; stderr sigue yendo a la pantalla.Por defectotecladols0 stdin1 stdout2 stderrpantallapantallapantalla: el error y accesos.logCon > salida.txttecladols0 stdin1 stdout2 stderrsalida.txtpantallasalida.txt: accesos.log · pantalla: el error
> solo mueve el flujo 1: el error viaja por el 2 y por eso sigue saliendo en pantalla.

Si alguna vez redirigiste algo a un fichero y «se te escapó» un mensaje a la pantalla, casi seguro era stderr. No es un fallo de la redirección: es que ese mensaje viajaba por el otro flujo.

> y >>: escribir la salida en un fichero

> y >> mandan stdout a un fichero. La diferencia es qué hacen si el fichero ya existe:

# > crea o vacía; >> añade al final
echo "Informe del aula 1.3" > informe.txt  # > crea el fichero (o lo vacía si ya existía)
echo "Lunes 19 de octubre" >> informe.txt  # >> añade al final sin tocar lo anterior
cat informe.txt
echo "Martes 20 de octubre" > informe.txt  # otra vez >: lo de antes desaparece
cat informe.txt
set -o noclobber                           # red de seguridad: > ya no pisa ficheros que existen
echo "Intento pisarlo" > informe.txt
echo "Esto sí entra" >| informe.txt        # >| fuerza la escritura aunque noclobber esté activo
cat informe.txt
set +o noclobber                           # la quitamos para no liarnos en el resto de la sesión

Y esto es lo que salió:

$ echo "Informe del aula 1.3" > informe.txt
$ echo "Lunes 19 de octubre" >> informe.txt
$ cat informe.txt
Informe del aula 1.3
Lunes 19 de octubre
$ echo "Martes 20 de octubre" > informe.txt
$ cat informe.txt
Martes 20 de octubre
$ set -o noclobber
$ echo "Intento pisarlo" > informe.txt
bash: informe.txt: cannot overwrite existing file
$ echo "Esto sí entra" >| informe.txt
$ cat informe.txt
Esto sí entra
$ set +o noclobber

Línea a línea:

  1. El primer echo … > informe.txt crea el fichero con una línea.
  2. El echo … >> informe.txt añade la segunda línea al final. El cat muestra las dos.
  3. El tercer echo vuelve a usar >, y > vacía el fichero antes de escribir: el cat ya solo muestra «Martes 20 de octubre». Lo del lunes se ha perdido para siempre.
  4. Con set -o noclobber activamos una red de seguridad: a partir de ahí > se niega a pisar un fichero que ya existe (cannot overwrite existing file). Si de verdad quieres pisarlo, >| lo fuerza.
OperadorSi el fichero no existeSi el fichero ya existe
>lo crealo vacía y escribe desde el principio
>>lo creaescribe al final, sin tocar lo que había
>|lo crealo vacía aunque esté activo noclobber

Ahora sí podemos explicar el gancho. Cuando escribes sort usuarios.txt > usuarios.txt, bash hace las cosas en este orden:

  1. Lee la línea y ve > usuarios.txt.
  2. Abre usuarios.txt para escritura y lo vacía (eso es lo que hace >).
  3. Solo entonces arranca sort, que abre usuarios.txt para leer… y lo encuentra vacío.
  4. sort ordena cero líneas y escribe cero líneas. Resultado: 0 bytes.

Leer y escribir el mismo fichero en una orden (sort f > f, grep x f > f, cut … f > f). El > lo vacía antes de que la orden lo lea. Escribe en otro fichero y luego renómbralo (sort f > f.tmp && mv f.tmp f) o usa la opción propia de la orden si la tiene (sort -o f f sí funciona, porque sort lee todo antes de escribir).

Pon set -o noclobber en tu ~/.bashrc mientras aprendes. Te avisará cada vez que un > vaya a pisar algo, y cuando lo hagas a propósito usarás >| sabiendo lo que haces.

<: leer la entrada desde un fichero

< es el espejo de >: conecta un fichero al flujo 0, la entrada estándar.

# < conecta un fichero a la entrada estándar (stdin, descriptor 0)
wc -l accesos.log          # wc abre el fichero él mismo, por eso escribe su nombre
wc -l < accesos.log        # con < lo abre bash: wc solo recibe datos, sin nombre
sort < usuarios.txt        # sort lee de stdin si no le das un fichero
tr a-z A-Z < usuarios.txt  # tr SOLO lee de stdin: no admite nombres de fichero

La salida:

$ wc -l accesos.log
16 accesos.log
$ wc -l < accesos.log
16
$ sort < usuarios.txt
ana
dani
laura
pablo
sergio
$ tr a-z A-Z < usuarios.txt
PABLO
LAURA
SERGIO
ANA
DANI

¿Por qué wc -l accesos.log escribe 16 accesos.log y wc -l < accesos.log solo 16? Porque son dos situaciones distintas:

  • En la primera, wc abre el fichero él mismo: sabe cómo se llama y por eso lo pone en la salida.
  • En la segunda, lo abre bash y se lo conecta a stdin. wc solo ve un chorro de datos que llega por su entrada; no tiene ni idea de que venía de un fichero, así que no puede escribir ningún nombre.

Esto, que parece una tontería, es muy útil cuando quieres el número a secas para meterlo en un informe (lo haremos en la práctica). 16 líneas, por cierto, son las 16 entradas del registro.

sort < usuarios.txt ordena igual que sort usuarios.txt: casi todos los filtros aceptan las dos formas. Pero tr (que cambia unos caracteres por otros; aquí a-z por A-Z, minúsculas a mayúsculas) solo lee de stdin: si le pasas un nombre de fichero, se queja. Por eso hay que usar <.

Fíjate en que usuarios.txt aquí sí está entero: cada ejemplo de este artículo empieza con una copia limpia de los ficheros. El vaciado del gancho no se arrastra.

2>, 2>&1 y /dev/null: qué hacer con los errores

Si > es lo mismo que 1> (el 1 se puede omitir), para redirigir stderr basta con poner su número: 2>. Y para mandar los dos flujos al mismo sitio existe 2>&1, que es justo lo que más se lía. Esta es la sesión completa:

# 2>, 2>&1, &> y /dev/null
ls accesos.log noexiste 2> errores.txt          # 2> desvía solo stderr (el descriptor 2)
cat errores.txt
ls accesos.log noexiste > todo.txt 2>&1         # 1 al fichero y DESPUÉS 2 a donde apunte 1
cat todo.txt
ls accesos.log noexiste 2>&1 > solo-uno.txt     # al revés: 2 copia la pantalla ANTES de mover 1
cat solo-uno.txt
ls accesos.log noexiste &> todo2.txt            # atajo de bash para > todo2.txt 2>&1
cat todo2.txt
ls noexiste 2> /dev/null                        # /dev/null se traga todo lo que le mandes
echo $?                                         # el error ha ocurrido igual: código de salida 2
wc -c /dev/null                                 # y siempre está vacío

Y su salida real:

$ ls accesos.log noexiste 2> errores.txt
accesos.log
$ cat errores.txt
ls: cannot access 'noexiste': No such file or directory
$ ls accesos.log noexiste > todo.txt 2>&1
$ cat todo.txt
ls: cannot access 'noexiste': No such file or directory
accesos.log
$ ls accesos.log noexiste 2>&1 > solo-uno.txt
ls: cannot access 'noexiste': No such file or directory
$ cat solo-uno.txt
accesos.log
$ ls accesos.log noexiste &> todo2.txt
$ cat todo2.txt
ls: cannot access 'noexiste': No such file or directory
accesos.log
$ ls noexiste 2> /dev/null
$ echo $?
2
$ wc -c /dev/null
0 /dev/null

Lo vemos en cuatro bloques.

2> errores.txt. Solo se desvía stderr. En pantalla queda accesos.log (stdout) y el error acaba dentro de errores.txt. Es justo lo contrario de lo que pasaba con >.

> todo.txt 2>&1. Los dos flujos van a todo.txt; en pantalla no sale nada. Fíjate en que dentro del fichero el error va primero: ls se queja de los ficheros que no encuentra antes de listar los que sí.

2>&1 > solo-uno.txt. ¡Es lo mismo pero en otro orden, y el error sale en pantalla! Esta es la parte que más cuesta, así que vamos despacio. La clave es leer 2>&1 como «haz que el 2 apunte a donde apunte el 1 en este momento», no como «el 2 sigue al 1 para siempre». Y bash procesa las redirecciones de izquierda a derecha:

Por qué importa el orden de 2>&1Bash procesa las redirecciones de izquierda a derecha. En la primera orden, 1 pasa a todo.txt y después 2 copia ese destino, así que los dos van al fichero. En la segunda, 2 copia el destino de 1 cuando aún es la pantalla y después 1 se va al fichero, así que el error sigue en pantalla.$ ls accesos.log noexiste > todo.txt 2>&1Al empezar1 → pantalla2 → pantalla> todo.txt1 → todo.txt2 → pantalla2>&11 → todo.txt2 → todo.txt  todo.txt recibe la salida y el error$ ls accesos.log noexiste 2>&1 > solo-uno.txtAl empezar1 → pantalla2 → pantalla2>&11 → pantalla2 → pantalla> solo-uno.txt1 → solo-uno.txt2 → pantalla  el error sale en pantalla: solo-uno.txt solo tiene accesos.log
2>&1 significa «2 apunta a donde apunte 1 AHORA», no «2 sigue a 1 para siempre».

Como tabla de traza, paso a paso:

OrdenPasoFlujo 1 apunta aFlujo 2 apunta a
> todo.txt 2>&1al empezarpantallapantalla
> todo.txttodo.txtpantalla
2>&1 (copia el 1 actual)todo.txttodo.txt
2>&1 > solo-uno.txtal empezarpantallapantalla
2>&1 (copia el 1 actual)pantallapantalla (no cambia nada)
> solo-uno.txtsolo-uno.txtpantalla

En el segundo caso, cuando se ejecuta 2>&1 el flujo 1 todavía apunta a la pantalla, así que el 2 «copia» la pantalla. Después el 1 se va al fichero, pero el 2 ya tenía su destino guardado y no se entera. Por eso solo-uno.txt solo contiene accesos.log.

Escribir orden 2>&1 > fichero pensando que guarda todo. Guarda solo stdout. La regla para no fallar: primero el fichero, luego 2>&1: orden > fichero 2>&1.

&> todo2.txt. Es un atajo de bash que significa exactamente > todo2.txt 2>&1. Más corto y sin posibilidad de equivocarte con el orden. Ojo: es de bash; en un sh estricto no existe, así que en scripts que tengan que funcionar en cualquier sitio verás la forma larga.

/dev/null. Es un fichero especial que se traga todo lo que le escribes y, si lo lees, está siempre vacío (wc -c /dev/null da 0). ls noexiste 2> /dev/null no muestra nada… pero el error ha ocurrido igual: echo $? muestra el código de salida de la última orden, y es 2 (en ls, 0 = todo bien, 2 = problema serio, como un fichero que no existe). /dev/null silencia el mensaje, no arregla el fallo.

2> /dev/null es perfecto cuando ya sabes que va a haber errores que no te importan (por ejemplo, buscar en carpetas sin permiso de lectura, como vimos en Permisos). Pero no lo pongas «por si acaso»: si algo falla de verdad, no te enterarás.

Tuberías: la salida de una orden es la entrada de la siguiente

Hasta ahora conectábamos programas con ficheros. Una tubería (|, la barra vertical, AltGr + 1 en el teclado español) conecta el stdout de una orden con el stdin de la siguiente, sin fichero intermedio. Es la idea más potente de la terminal: muchas herramientas pequeñas que hacen una sola cosa, encadenadas.

Vamos a responder a una pregunta: ¿quién entra más veces en el aula? La construimos paso a paso, añadiendo una orden cada vez:

# Una tubería construida paso a paso: quién entra más en el aula
cut -d' ' -f4 accesos.log                                # paso 1: solo la columna 4 (usuario)
cut -d' ' -f4 accesos.log | sort                         # paso 2: ordenados, los iguales quedan juntos
cut -d' ' -f4 accesos.log | sort | uniq -c               # paso 3: uniq -c cuenta cada grupo de iguales
cut -d' ' -f4 accesos.log | sort | uniq -c | sort -nr    # paso 4: de más a menos accesos
cut -d' ' -f4 accesos.log | uniq -c                      # sin sort: uniq solo junta repetidos SEGUIDOS

La salida (larga, pero merece la pena mirarla entera):

$ cut -d' ' -f4 accesos.log
sergio
laura
laura
pablo
ana
ana
ana
sergio
dani
dani
laura
laura
laura
pablo
sergio
ana
$ cut -d' ' -f4 accesos.log | sort
ana
ana
ana
ana
dani
dani
laura
laura
laura
laura
laura
pablo
pablo
sergio
sergio
sergio
$ cut -d' ' -f4 accesos.log | sort | uniq -c
      4 ana
      2 dani
      5 laura
      2 pablo
      3 sergio
$ cut -d' ' -f4 accesos.log | sort | uniq -c | sort -nr
      5 laura
      4 ana
      3 sergio
      2 pablo
      2 dani
$ cut -d' ' -f4 accesos.log | uniq -c
      1 sergio
      2 laura
      1 pablo
      3 ana
      1 sergio
      2 dani
      3 laura
      1 pablo
      1 sergio
      1 ana
  • Paso 1: cut -d' ' -f4 corta cada línea por los espacios (-d' ', el delimitador) y se queda con el campo 4, el usuario. 16 líneas, una por registro.
  • Paso 2: sort las ordena alfabéticamente. Lo importante no es el orden en sí, sino que los nombres iguales quedan juntos: las cuatro ana seguidas, las dos dani…
  • Paso 3: uniq -c recorre las líneas y, cada vez que se repite la anterior, suma 1. Con -c pone delante cuántas había en cada grupo: 4 + 2 + 5 + 2 + 3 = 16, las mismas 16 líneas de antes.
  • Paso 4: sort -nr ordena esos recuentos como números (-n) y al revés (-r), de más a menos. Laura gana con 5 registros.
Una tubería, paso a pasoCuatro órdenes unidas por tuberías. cut saca la columna de usuario (16 líneas), sort las ordena para que los nombres iguales queden juntos, uniq -c cuenta cada grupo (5 líneas) y sort -nr las ordena de más a menos: laura 5, ana 4, sergio 3, pablo 2, dani 2.$ cut -d' ' -f4 accesos.log | sort | uniq -c | sort -nrcut -d' ' -f4sergiolauralaurapabloanaana… 16 líneassortanaanaanaanadanidani… 16 líneasuniq -c4 ana2 dani5 laura2 pablo3 sergio5 líneassort -nr5 laura4 ana3 sergio2 pablo2 dani5 líneas
Cada | pasa la salida de una orden a la siguiente; uniq -c solo puede contar bien porque sort ya juntó los iguales.

¿Y el último experimento, sin sort? uniq -c da 10 grupos y sergio sale tres veces con un 1. No es un fallo de uniq: uniq solo compara cada línea con la anterior. Si dos líneas iguales no están pegadas, para él son grupos distintos. Por eso casi siempre lo verás detrás de un sort: sort | uniq -c.

Usar uniq sin sort delante y fiarte del resultado. Con datos que ya vienen agrupados parece funcionar, y el día que no lo están te da recuentos partidos sin avisar.

Qué viaja por la tubería (y qué no)

Una tubería conecta stdout. ¿Y stderr? Sigue yendo a la pantalla:

# Por la tubería solo viaja stdout
ls accesos.log noexiste | wc -l               # el error no entra: sale en pantalla y wc cuenta 1 línea
ls accesos.log noexiste 2>&1 | wc -l          # 2>&1 antes de |: el error también viaja, 2 líneas
ls accesos.log noexiste 2> /dev/null | wc -l  # el error a la basura; wc sigue contando 1

La salida:

$ ls accesos.log noexiste | wc -l
ls: cannot access 'noexiste': No such file or directory
1
$ ls accesos.log noexiste 2>&1 | wc -l
2
$ ls accesos.log noexiste 2> /dev/null | wc -l
1
  • En la primera orden, el error sale en pantalla y wc -l cuenta 1 línea: la única que recibió fue accesos.log. El mensaje de error nunca entró en la tubería.
  • Con 2>&1 antes de |, stderr se suma a stdout y los dos viajan: wc -l cuenta 2. (Aquí el orden no da problemas: bash conecta la tubería al flujo 1 antes de procesar el 2>&1, así que el 2 copia «la tubería».)
  • Con 2> /dev/null, el error va a la basura y wc sigue contando 1, pero la pantalla queda limpia.

En bash, orden |& otra es el atajo de orden 2>&1 | otra, igual que &> lo era de > f 2>&1.

Los filtros que más vas a usar

Un filtro es una orden que lee texto (de un fichero o de stdin), lo transforma y lo escribe por stdout. Por eso encajan tan bien en tuberías.

grep, wc, head, tail y cut

# grep, wc, head, tail y cut sobre el registro del aula
grep FAIL accesos.log                    # las líneas que contienen FAIL
grep -c FAIL accesos.log                 # -c: solo cuántas son
grep -v LOGIN accesos.log                # -v invierte: las que NO contienen LOGIN
grep -ic fail accesos.log                # -i ignora mayúsculas y minúsculas
wc accesos.log                           # líneas, palabras y bytes
tail -n 3 accesos.log                    # las tres últimas líneas
cut -d' ' -f2,4 accesos.log | head -4    # columnas 2 y 4 (hora y usuario) de las 4 primeras
cut -c12-16 accesos.log | head -3        # por posición: los caracteres 12 a 16 son la hora

La salida:

$ grep FAIL accesos.log
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
$ grep -c FAIL accesos.log
5
$ grep -v LOGIN accesos.log
2026-10-19 09:58 pc01 sergio LOGOUT OK
2026-10-19 10:15 pc02 laura LOGOUT OK
2026-10-19 11:40 pc03 pablo LOGOUT OK
2026-10-19 12:00 pc04 ana LOGOUT OK
$ grep -ic fail accesos.log
5
$ wc accesos.log
 16  96 599 accesos.log
$ tail -n 3 accesos.log
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
$ cut -d' ' -f2,4 accesos.log | head -4
08:02 sergio
08:05 laura
08:06 laura
08:11 pablo
$ cut -c12-16 accesos.log | head -3
08:02
08:05
08:06
  • grep FAIL deja pasar las líneas que contienen FAIL: los cinco intentos fallidos. Con -c solo dice cuántos (5) y con -v invierte el filtro: las 4 líneas que no contienen LOGIN son los LOGOUT. Con -i no distingue mayúsculas: fail también encuentra FAIL.
  • wc sin opciones da líneas, palabras y bytes: 16 96 599. Cuadra: 16 líneas × 6 palabras = 96. Y los bytes: cada línea mide entre 35 y 39 caracteres más el salto de línea, y sumadas dan 599 (la primera, 2026-10-19 08:02 pc01 sergio LOGIN OK, tiene 37 caracteres + 1 salto = 38).
  • tail -n 3 da las 3 últimas líneas (y head -n 3, o head -3, las 3 primeras).
  • cut -d' ' -f2,4 saca varios campos: la hora y el usuario. cut -c12-16 corta por posición de carácter: en todas las líneas los caracteres 12 a 16 son la hora (la fecha ocupa del 1 al 10 y el 11 es un espacio).
Quiero…Orden
líneas que contienen un textogrep texto f
cuántas líneas lo contienengrep -c texto f
las que NO lo contienengrep -v texto f
contar líneas / palabras / byteswc -l / wc -w / wc -c
las N primeras / últimashead -n N / tail -n N
desde la línea N hasta el finaltail -n +N
una columna separada por un caráctercut -d'X' -fN

sort, uniq y tee

# sort, uniq y tee
sort -r usuarios.txt                                      # orden alfabético inverso
printf '10\n9\n100\n' | sort                             # sort compara texto: "100" va antes que "9"
printf '10\n9\n100\n' | sort -n                          # -n compara números
cut -d' ' -f3 accesos.log | sort -u                       # -u: sin repetidos (como sort | uniq)
grep FAIL accesos.log | cut -d' ' -f4 | sort | uniq -d    # -d: solo los que aparecen más de una vez
grep FAIL accesos.log | tee fallos.txt | wc -l            # tee guarda una copia y deja seguir los datos
cat fallos.txt

La salida:

$ sort -r usuarios.txt
sergio
pablo
laura
dani
ana
$ printf '10\n9\n100\n' | sort
10
100
9
$ printf '10\n9\n100\n' | sort -n
9
10
100
$ cut -d' ' -f3 accesos.log | sort -u
pc01
pc02
pc03
pc04
$ grep FAIL accesos.log | cut -d' ' -f4 | sort | uniq -d
ana
laura
$ grep FAIL accesos.log | tee fallos.txt | wc -l
5
$ 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
  • sort -r invierte el orden.
  • El experimento con printf es el clásico: sort sin opciones compara texto, carácter a carácter. «100» empieza por «1», que va antes que «9», así que queda antes. Con -n compara números y el orden es el que esperabas: 9, 10, 100.
  • sort -u ordena y quita repetidos de una vez: los 4 ordenadores.
  • uniq -d muestra solo las líneas que se repiten: ana y laura han fallado más de una vez (dani, solo una).
  • tee fallos.txt es una «T» de fontanero: escribe lo que le llega en el fichero y también por stdout, así que la tubería sigue. wc -l cuenta 5 y, a la vez, fallos.txt se ha quedado con las 5 líneas.

tee -a fichero añade al final en vez de vaciar, igual que >> frente a >.

Extra: here-documents (<<)

A veces quieres darle a una orden varias líneas de texto sin crear antes un fichero. Para eso está el here-document: << EOF le dice a bash «lo que escriba a continuación, hasta una línea que diga solo EOF, es la entrada estándar de esta orden». La palabra EOF es una costumbre; puede ser cualquiera.

# Here-documents: el texto de entrada va escrito dentro de la propia orden
cat << EOF > nota.txt
Revisión del aula hecha por $USER
Ordenadores: pc01 a pc04
EOF
cat nota.txt
cat << 'EOF'
Con comillas no se sustituye: $USER
EOF
wc -l << EOF
uno
dos
tres
EOF

Esto es lo que se ve en la terminal (los > del principio de línea son el indicador de «sigue escribiendo» de bash, no redirecciones):

$ cat << EOF > nota.txt
> Revisión del aula hecha por $USER
> Ordenadores: pc01 a pc04
> EOF
$ cat nota.txt
Revisión del aula hecha por sergio
Ordenadores: pc01 a pc04
$ cat << 'EOF'
> Con comillas no se sustituye: $USER
> EOF
Con comillas no se sustituye: $USER
$ wc -l << EOF
> uno
> dos
> tres
> EOF
3
  • El primero combina << con >: crea nota.txt con dos líneas, y $USER se ha cambiado por sergio.
  • En el segundo, con 'EOF' entre comillas, bash no sustituye nada: sale $USER tal cual. Útil cuando el texto lleva símbolos $ que no quieres que se interpreten.
  • El tercero alimenta a wc -l con tres líneas: 3.

Lo usarás mucho en los scripts, el siguiente tema, para escribir cabeceras de informes o ficheros de configuración.

¿Cuándo uso cada cosa?

SituaciónUsaPor qué
Guardar un resultado, empezando de ceroorden > fvacía y escribe
Ir acumulando resultadosorden >> fañade al final
Que un programa lea de un fichero sin ver su nombreorden < flo abre bash
Separar los erroresorden 2> errores.txtsolo el flujo 2
Guardarlo todo en un ficheroorden > f 2>&1 (o &> f)1 al fichero y luego 2 a donde el 1
Ignorar errores que no importanorden 2> /dev/nullse los traga
Procesar la salida con otra ordenorden | filtrosin fichero intermedio
Que los errores también entren en la tuberíaorden 2>&1 | filtrostderr se suma a stdout
Guardar y seguir procesando… | tee f | …copia y deja pasar
Contar repeticionessort | uniq -c | sort -nragrupa, cuenta y ordena

Errores típicos

sort f > f (o cualquier orden que lea y escriba el mismo fichero). El > vacía f antes de que la orden lo lea y te quedas con 0 bytes. Escribe en otro fichero.

orden 2>&1 > f para guardarlo todo. El orden importa: primero > f, después 2>&1.

uniq sin sort delante. Solo junta repetidos seguidos.

sort sin -n para números: «100» queda antes que «9» porque compara texto.

Esperar que un error entre en la tubería. Por | solo viaja stdout; si quieres los errores, 2>&1 antes de la barra.

Pruébalo en tu terminal

Python Tutor no sirve para la terminal, pero la terminal es aún mejor: la tienes delante. Abre una (en Linux, en el WSL de Windows o en la máquina virtual de clase), métete en una carpeta de pruebas y teclea estas órdenes una a una. Esta es la transcripción de lo que deberías ver (los mensajes de error saldrán en tu idioma):

$ printf 'ana\nlaura\nana\n' > prueba.txt
$ uniq -c prueba.txt
      1 ana
      1 laura
      1 ana
$ sort prueba.txt | uniq -c
      2 ana
      1 laura
$ ls prueba.txt noexiste > out.txt 2>&1
$ cat out.txt
ls: cannot access 'noexiste': No such file or directory
prueba.txt
$ ls prueba.txt noexiste 2>&1 > out.txt
ls: cannot access 'noexiste': No such file or directory
$ cat out.txt
prueba.txt

Qué observar:

  1. printf 'ana\nlaura\nana\n' escribe tres líneas (\n es el salto de línea) y el > las guarda en prueba.txt.
  2. uniq -c prueba.txt cuenta tres grupos de 1: las dos ana no están pegadas. Con sort delante salen 2 ana y 1 laura.
  3. Con > out.txt 2>&1 la pantalla queda en silencio y out.txt tiene dos líneas: el error y prueba.txt.
  4. Con 2>&1 > out.txt el error aparece en pantalla y el cat final ya solo muestra prueba.txt. Cambia una sola cosa (el orden) y cambia el resultado.
  5. Bonus: repite la última orden con &> out.txt y comprueba que se comporta como la tercera.

Resumen

ConceptoEn una frase
Flujos0 stdin (teclado), 1 stdout y 2 stderr (pantalla); los conecta bash antes de arrancar la orden
> / >>stdout a un fichero: > lo vacía primero, >> añade al final
<un fichero como stdin; la orden no ve su nombre
2>solo los errores a un fichero
2>&1el 2 apunta a donde apunte el 1 en ese momento: > f 2>&1, no al revés
/dev/nullse traga lo que le mandes; el error sigue ocurriendo ($?)
|stdout de una orden → stdin de la siguiente; stderr no viaja
Filtrosgrep, wc, head, tail, cut, sort, uniq, tee
<< EOFvarias líneas de entrada escritas en la propia orden

Tres ideas para llevarte

  1. Bash prepara las redirecciones antes de arrancar la orden. Por eso sort f > f vacía el fichero y por eso el orden de 2>&1 importa.
  2. stdout y stderr son dos flujos distintos aunque se vean en la misma pantalla. > y | solo tocan el 1; los errores necesitan su 2>.
  3. Las tuberías se construyen paso a paso. Añade una orden, mira la salida, añade la siguiente. Y si vas a contar, sort | uniq -c.

Ya sabes conectar órdenes entre sí y con ficheros. El siguiente paso es guardarlas en un fichero y ejecutarlas todas de golpe: los shell scripts, el próximo tema de Linux. Antes, practica con la práctica de redirecciones y tuberías: tres programas completos sobre el mismo registro del aula.

Siguiente paso: prácticaRedirecciones 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)

¿Te ha servido este artículo? Así puedes apoyar SergioLearns →

Sigue aprendiendo

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *