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.
Tabla de Contenidos
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:
| Columna | Qué es | Ejemplo |
|---|---|---|
| 1 | fecha | 2026-10-19 |
| 2 | hora | 08:05 |
| 3 | ordenador | pc02 |
| 4 | usuario | laura |
| 5 | evento | LOGIN o LOGOUT |
| 6 | resultado | OK 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úmero | Nombre | Para qué sirve | A dónde va por defecto |
|---|---|---|---|
| 0 | stdin (entrada estándar) | de donde el programa lee | el teclado |
| 1 | stdout (salida estándar) | los resultados normales | la pantalla |
| 2 | stderr (salida de error) | los mensajes de error y avisos | la 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 noexistepide dos cosas. El ficheroaccesos.logexiste, así quelsescribe su nombre por stdout.noexisteno existe, así quelsescribe 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.txtmanda stdout al fichero. Pero el error sigue saliendo en pantalla:>solo toca el flujo 1.cat salida.txtlo 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.
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:
- El primer
echo … > informe.txtcrea el fichero con una línea. - El
echo … >> informe.txtañade la segunda línea al final. Elcatmuestra las dos. - El tercer
echovuelve a usar>, y>vacía el fichero antes de escribir: elcatya solo muestra «Martes 20 de octubre». Lo del lunes se ha perdido para siempre. - Con
set -o noclobberactivamos 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.
| Operador | Si el fichero no existe | Si el fichero ya existe |
|---|---|---|
> | lo crea | lo vacía y escribe desde el principio |
>> | lo crea | escribe al final, sin tocar lo que había |
>| | lo crea | lo 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:
- Lee la línea y ve
> usuarios.txt. - Abre
usuarios.txtpara escritura y lo vacía (eso es lo que hace>). - Solo entonces arranca
sort, que abreusuarios.txtpara leer… y lo encuentra vacío. sortordena 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,
wcabre 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.
wcsolo 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:
Como tabla de traza, paso a paso:
| Orden | Paso | Flujo 1 apunta a | Flujo 2 apunta a |
|---|---|---|---|
> todo.txt 2>&1 | al empezar | pantalla | pantalla |
> todo.txt | todo.txt | pantalla | |
2>&1 (copia el 1 actual) | todo.txt | todo.txt | |
2>&1 > solo-uno.txt | al empezar | pantalla | pantalla |
2>&1 (copia el 1 actual) | pantalla | pantalla (no cambia nada) | |
> solo-uno.txt | solo-uno.txt | pantalla |
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' ' -f4corta 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:
sortlas ordena alfabéticamente. Lo importante no es el orden en sí, sino que los nombres iguales quedan juntos: las cuatroanaseguidas, las dosdani… - Paso 3:
uniq -crecorre las líneas y, cada vez que se repite la anterior, suma 1. Con-cpone delante cuántas había en cada grupo: 4 + 2 + 5 + 2 + 3 = 16, las mismas 16 líneas de antes. - Paso 4:
sort -nrordena esos recuentos como números (-n) y al revés (-r), de más a menos. Laura gana con 5 registros.
¿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 -lcuenta 1 línea: la única que recibió fueaccesos.log. El mensaje de error nunca entró en la tubería. - Con
2>&1antes de|, stderr se suma a stdout y los dos viajan:wc -lcuenta 2. (Aquí el orden no da problemas: bash conecta la tubería al flujo 1 antes de procesar el2>&1, así que el 2 copia «la tubería».) - Con
2> /dev/null, el error va a la basura ywcsigue 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 FAILdeja pasar las líneas que contienenFAIL: los cinco intentos fallidos. Con-csolo dice cuántos (5) y con-vinvierte el filtro: las 4 líneas que no contienenLOGINson losLOGOUT. Con-ino distingue mayúsculas:failtambién encuentraFAIL.wcsin 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 3da las 3 últimas líneas (yhead -n 3, ohead -3, las 3 primeras).cut -d' ' -f2,4saca varios campos: la hora y el usuario.cut -c12-16corta 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 texto | grep texto f |
| cuántas líneas lo contienen | grep -c texto f |
| las que NO lo contienen | grep -v texto f |
| contar líneas / palabras / bytes | wc -l / wc -w / wc -c |
| las N primeras / últimas | head -n N / tail -n N |
| desde la línea N hasta el final | tail -n +N |
| una columna separada por un carácter | cut -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 -rinvierte el orden.- El experimento con
printfes el clásico:sortsin opciones compara texto, carácter a carácter. «100» empieza por «1», que va antes que «9», así que queda antes. Con-ncompara números y el orden es el que esperabas: 9, 10, 100. sort -uordena y quita repetidos de una vez: los 4 ordenadores.uniq -dmuestra solo las líneas que se repiten:anaylaurahan fallado más de una vez (dani, solo una).tee fallos.txtes una «T» de fontanero: escribe lo que le llega en el fichero y también por stdout, así que la tubería sigue.wc -lcuenta 5 y, a la vez,fallos.txtse 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>: creanota.txtcon dos líneas, y$USERse ha cambiado porsergio. - En el segundo, con
'EOF'entre comillas, bash no sustituye nada: sale$USERtal cual. Útil cuando el texto lleva símbolos$que no quieres que se interpreten. - El tercero alimenta a
wc -lcon 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ón | Usa | Por qué |
|---|---|---|
| Guardar un resultado, empezando de cero | orden > f | vacía y escribe |
| Ir acumulando resultados | orden >> f | añade al final |
| Que un programa lea de un fichero sin ver su nombre | orden < f | lo abre bash |
| Separar los errores | orden 2> errores.txt | solo el flujo 2 |
| Guardarlo todo en un fichero | orden > f 2>&1 (o &> f) | 1 al fichero y luego 2 a donde el 1 |
| Ignorar errores que no importan | orden 2> /dev/null | se los traga |
| Procesar la salida con otra orden | orden | filtro | sin fichero intermedio |
| Que los errores también entren en la tubería | orden 2>&1 | filtro | stderr se suma a stdout |
| Guardar y seguir procesando | … | tee f | … | copia y deja pasar |
| Contar repeticiones | sort | uniq -c | sort -nr | agrupa, 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:
printf 'ana\nlaura\nana\n'escribe tres líneas (\nes el salto de línea) y el>las guarda enprueba.txt.uniq -c prueba.txtcuenta tres grupos de 1: las dosanano están pegadas. Consortdelante salen2 anay1 laura.- Con
> out.txt 2>&1la pantalla queda en silencio yout.txttiene dos líneas: el error yprueba.txt. - Con
2>&1 > out.txtel error aparece en pantalla y elcatfinal ya solo muestraprueba.txt. Cambia una sola cosa (el orden) y cambia el resultado. - Bonus: repite la última orden con
&> out.txty comprueba que se comporta como la tercera.
Resumen
| Concepto | En una frase |
|---|---|
| Flujos | 0 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>&1 | el 2 apunta a donde apunte el 1 en ese momento: > f 2>&1, no al revés |
/dev/null | se traga lo que le mandes; el error sigue ocurriendo ($?) |
| | stdout de una orden → stdin de la siguiente; stderr no viaja |
| Filtros | grep, wc, head, tail, cut, sort, uniq, tee |
<< EOF | varias líneas de entrada escritas en la propia orden |
Tres ideas para llevarte
- Bash prepara las redirecciones antes de arrancar la orden. Por eso
sort f > fvacía el fichero y por eso el orden de2>&1importa. - stdout y stderr son dos flujos distintos aunque se vean en la misma pantalla.
>y|solo tocan el 1; los errores necesitan su2>. - 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 →




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)