← Terminal y Línea de Comandos en Linux Bloque 6 · Filtrar y transformar texto · Tema 31 de 43

Tema 31

Comparar y combinar archivos

Un archivo de configuración funcionaba ayer y hoy no: ¿qué línea cambió? Hay dos copias de un informe con el mismo nombre: ¿son iguales? Una lista de inscriptos y otra de quienes pagaron: ¿quién falta? Todos los filtros del bloque miraron un archivo por vez. Estas preguntas necesitan mirar dos, y ponerlos uno frente al otro. Con las cuatro herramientas que lo hacen —cmp, diff, comm y join— se completa el trabajo con texto.

1. Dos archivos a la vez

«Comparar» no es una sola operación. Según lo que se quiera saber, y según qué clase de archivos se tenga delante, corresponde una herramienta distinta. Conviene tener el mapa antes de empezar:

HerramientaResponde aMira los archivos comoExige orden
cmp¿Son idénticos?Secuencias de bytes. Sirve para cualquier archivo.No
diff¿Qué líneas cambiaron, y dónde?Textos, donde la posición de cada línea importa.No
comm¿Qué elementos están en uno, en otro, o en los dos?Listas, donde la posición no importa.Sí
join¿Qué dice el otro archivo sobre esta misma clave?Tablas con un campo en común.Sí

Las tres primeras comparan: informan sobre las diferencias. La última combina: produce un archivo nuevo con datos de los dos. La distinción entre diff y comm es la más fina, y la que más importa elegir bien. Un programa, una carta o un archivo de configuración son textos: si dos líneas cambian de lugar, el archivo es otro. Una nómina de alumnos o una lista de paquetes instalados son conjuntos: lo que interesa es quién está, no en qué renglón.

Las cuatro tienen la misma forma, comando archivo1 archivo2, y ninguna modifica los archivos que recibe.

2. Los archivos de trabajo

Para comparar hacen falta parejas de archivos. Las fabricamos con lo aprendido en los temas anteriores, y las dejamos en /tmp para no alterar el directorio de práctica. La primera pareja es tareas.txt y una versión posterior, a la que se le quitó una tarea, se le corrigió otra y se le agregó una al final:

terminal, en ~/practica
$ cd ~/practica
$ cp tareas.txt /tmp/copia.txt                                      # una copia exacta
$ sed -e '2d' -e 's/vim/awk/' -e '$a Sacar la basura' tareas.txt > /tmp/tareas2.txt
$ cat /tmp/tareas2.txt
Comprar pan
Pagar la luz
Estudiar awk
Regar las plantas
Sacar la basura

Las otras parejas salen de ventas.csv. Con awk obtenemos dos listas de productos, los vendidos en cada ciudad, y el total por vendedor. A mano escribimos una tabla con el objetivo de ventas de cada vendedor, donde figura uno, Pedro, que todavía no vendió nada:

terminal
$ awk -F, '$3 == "Rosario" {print $4}' ventas.csv | sort -u > /tmp/rosario.txt
$ awk -F, '$3 == "Mendoza" {print $4}' ventas.csv | sort -u > /tmp/mendoza.txt
$ awk -F, '{t[$2] += $6} END {for (v in t) print v "," t[v]}' ventas.csv | sort > /tmp/totales.csv
$ cat > /tmp/objetivos.csv << FIN
> Ana,600
> Luis,500
> Marta,350
> Pedro,300
> FIN
$ cat /tmp/totales.csv
Ana,731
Luis,495
Marta,395

Los tres archivos generados pasaron por sort, y el que escribimos a mano está en orden alfabético. No es un detalle de prolijidad: es el requisito de comm y de join, como veremos.

3. cmp: ¿son iguales?

La pregunta más simple admite solo sí o no. La responde cmp, de compare, que recorre los dos archivos byte por byte y se detiene en el primero que no coincide.

terminal
$ cmp tareas.txt /tmp/copia.txt              # sin salida: son idénticos
$ cmp tareas.txt /tmp/tareas2.txt
tareas.txt /tmp/tareas2.txt differ: byte 13, line 2

Como tantos comandos de Unix, cmp calla cuando no hay nada que decir: el silencio significa «iguales». Cuando difieren informa dónde empieza la diferencia —aquí, en el byte 13, que es el primero de la línea 2— y no sigue. No dice qué cambió, ni si hay más cambios.

Esa brevedad es su virtud. cmp no interpreta el contenido, de modo que sirve para cualquier archivo: imágenes, comprimidos, ejecutables. Y es rápido, porque abandona en la primera diferencia. Es la manera de comprobar que una copia salió bien.

OpciónEfecto
NingunaInforma la primera diferencia, o no escribe nada.
-sSilencioso: no escribe nunca. El resultado se lee en el código de salida.
-lLista todos los bytes que difieren, no solo el primero.

Todo comando termina con un número, el código de salida, que la shell guarda en la variable $?: cero quiere decir «todo bien», y cualquier otro valor, que algo no resultó. En cmp y en diff el significado es el mismo:

CódigoSignifica
0Los archivos son iguales.
1Son distintos.
2Hubo un error: un archivo no existe o no se puede leer.
terminal
$ cmp -s tareas.txt /tmp/copia.txt; echo $?
0
$ cmp -s tareas.txt /tmp/tareas2.txt; echo $?
1
$ cmp -s tareas.txt /tmp/copia.txt && echo "La copia está bien"
La copia está bien

La tercera forma es la que se usa en los scripts: el operador && ejecuta el segundo comando solo si el primero terminó con cero, es decir, solo si los archivos son iguales.

Cuando los dos archivos no están en la misma máquina

cmp necesita tener los dos archivos a mano. Para comprobar que un archivo descargado es igual al que se publicó en un servidor se usa otro recurso: sha256sum archivo calcula a partir del contenido una «huella» de 64 caracteres. Dos archivos idénticos tienen la misma huella, y el menor cambio produce una completamente distinta. Basta entonces con comparar dos líneas cortas. Es lo que significan los archivos SHA256SUMS que acompañan a las imágenes de instalación de Linux.

4. diff: qué cambió

Saber que dos archivos difieren rara vez alcanza. Lo que se necesita es ver las diferencias, y esa es la tarea de diff, una de las herramientas más influyentes de Unix.

diff compara dos archivos de texto línea por línea y muestra el conjunto mínimo de cambios que habría que hacerle al primero para convertirlo en el segundo.

terminal
$ diff tareas.txt /tmp/copia.txt             # sin salida: son iguales
$ diff tareas.txt /tmp/tareas2.txt
2d1
< Llamar a Luis
4c3
< Estudiar vim
---
> Estudiar awk
5a5
> Sacar la basura

La definición tiene dos ideas que conviene separar. La primera es que diff no muestra los archivos: muestra solo lo que cambió. Las líneas que no se tocaron, como «Comprar pan» y «Regar las plantas», no figuran en la salida. En un archivo de mil líneas con una sola modificada, la salida tendría cuatro renglones.

La segunda es que la comparación tiene dirección. El primer argumento es el punto de partida, «lo viejo»; el segundo, el de llegada, «lo nuevo». La salida es una receta para ir de uno a otro, y por eso el orden de los argumentos importa: invertidos, cada cosa que se quitó aparece como agregada.

Encontrar ese conjunto mínimo no es trivial. diff no compara la línea 1 con la 1 y la 2 con la 2: si lo hiciera, al faltar la segunda línea todas las siguientes parecerían distintas. Busca la secuencia más larga de líneas que los dos archivos comparten en el mismo orden, la toma como esqueleto, y describe lo demás como cambios. Por eso entendió que «Pagar la luz» era la misma línea, aunque pasó del renglón 3 al 2.

5. Leer la salida: a, d y c

La salida anterior está en el llamado formato normal. Parece críptica y se lee con dos reglas. La primera: las líneas que empiezan con < son del primer archivo, y las que empiezan con >, del segundo. La segunda: cada cambio va precedido de una orden, formada por números de línea y una letra.

anatomía de una orden de diff
4c3
│││
││└─ la línea correspondiente en el segundo archivo
│└─ la operación: c, cambiar
└─ la línea del primer archivo
LetraViene deSignificaMuestra
aaddEn el segundo archivo hay líneas que el primero no tiene.Solo líneas con >.
ddeleteEn el primero hay líneas que el segundo no tiene.Solo líneas con <.
cchangeUnas líneas del primero fueron reemplazadas por otras.Las viejas con <, una raya ---, y las nuevas con >.

El número de la izquierda se refiere siempre al primer archivo, y el de la derecha, al segundo. Con eso, la salida se traduce renglón por renglón:

la salida, leída
2d1                  la línea 2 del primero se borró; habría quedado después de la 1 del segundo
< Llamar a Luis        esta es la línea que ya no está

4c3                  la línea 4 del primero cambió, y es ahora la 3 del segundo
< Estudiar vim         decía esto
---
> Estudiar awk         dice esto

5a5                  después de la línea 5 del primero se agregó la 5 del segundo
> Sacar la basura      esta es la línea nueva

Cuando el cambio abarca varias líneas, los números son rangos separados por una coma: 8,12c8,10 quiere decir que las líneas 8 a 12 del primero fueron reemplazadas por las 8 a 10 del segundo.

Las letras resultarán familiares. Son las órdenes a, d y c de sed, del tema 29, y no por casualidad: este formato nació como un guion para el editor ed, el antepasado común. Una regla para no confundir los signos: < apunta hacia la izquierda, hacia el primer argumento; >, hacia la derecha, hacia el segundo.

6. El formato unificado

El formato normal tiene un defecto: muestra los cambios sin lo que los rodea. Dice «la línea 4 cambió», pero no se ve qué hay en la 3 ni en la 5, y en un archivo grande eso obliga a ir a mirarlo. La opción -u produce un formato más legible, que es hoy el habitual.

terminal
$ diff -u tareas.txt /tmp/tareas2.txt
--- tareas.txt	2026-10-12 11:05:41.318402117 -0300
+++ /tmp/tareas2.txt	2026-10-20 09:14:02.551873340 -0300
@@ -1,5 +1,5 @@
 Comprar pan
-Llamar a Luis
 Pagar la luz
-Estudiar vim
+Estudiar awk
 Regar las plantas
+Sacar la basura

Aquí los dos archivos aparecen fundidos en uno, de ahí el nombre, y el primer carácter de cada línea dice a cuál pertenece:

La línea empieza conSignifica
--- y +++El encabezado: el nombre y la fecha del archivo viejo y del nuevo.
@@ -1,5 +1,5 @@El comienzo de un bloque de cambios, con su ubicación.
Un espacioUna línea de contexto: está en los dos archivos, sin cambios.
-Una línea que está solo en el archivo viejo: se quitó.
+Una línea que está solo en el nuevo: se agregó.

Una línea modificada se muestra como una que se quita y otra que se agrega, juntas: es lo que ocurre con «Estudiar vim» y «Estudiar awk». La marca del bloque se lee así: -1,5, «en el archivo viejo, desde la línea 1, cinco líneas»; +1,5, «en el nuevo, desde la línea 1, cinco líneas».

Alrededor de cada cambio se muestran tres líneas de contexto; -U 1 pide una sola, -U 0 ninguna. En un archivo largo, los cambios alejados entre sí forman bloques separados, cada uno con su marca @@, y lo que hay entre ellos se omite.

El formato que hay que saber leer

El formato unificado es el idioma en que se comunican los cambios en todo el mundo del software. Es lo que muestra git diff, lo que se ve al revisar una modificación en GitHub o GitLab, y lo que contienen los archivos .patch. Quien aprende a leer los signos - y + aquí, con dos archivos de cinco líneas, ya sabe leer cualquiera de esos. Con --color, diff pinta de rojo lo quitado y de verde lo agregado.

7. Lado a lado, y qué ignorar

Hay una tercera presentación, la más intuitiva para archivos cortos: los dos archivos en columnas paralelas. Se pide con -y, y -W fija el ancho total:

terminal
$ diff -y -W 50 tareas.txt /tmp/tareas2.txt
Comprar pan             Comprar pan
Llamar a Luis         <
Pagar la luz            Pagar la luz
Estudiar vim          | Estudiar awk
Regar las plantas       Regar las plantas
                      > Sacar la basura

En la columna central, < marca una línea que solo está a la izquierda, > una que solo está a la derecha, y | una que cambió. Con --suppress-common-lines se ocultan las que no tienen marca. Las líneas largas se cortan para caber: por eso este formato sirve para mirar, y no para guardar.

FormatoOpciónConviene para
NormalNingunaCambios pequeños; es el que aparece por defecto.
Unificado-uLeer, guardar y compartir cambios. La elección general.
Lado a lado-yRecorrer con la vista dos archivos de líneas cortas.
Solo un aviso-qSaber si difieren, sin el detalle.

El otro grupo de opciones decide qué cuenta como diferencia. Para diff, una línea con un espacio de más es otra línea. A veces esa exigencia estorba: se quiere saber si cambió el contenido, no la presentación.

OpciónIgnora
-iLas diferencias entre mayúsculas y minúsculas.
-bLos cambios en la cantidad de espacios: uno o varios da igual.
-wTodos los espacios, incluso si en un archivo los hay y en el otro no.
-BLas líneas vacías agregadas o quitadas.
--strip-trailing-crEl retorno de carro de los finales de línea de Windows.
terminal: una copia con una palabra en mayúsculas y espacios de más
$ sed 's/Comprar/COMPRAR/; s/Regar las/Regar   las/' tareas.txt > /tmp/t3.txt
$ diff tareas.txt /tmp/t3.txt
1c1
< Comprar pan
---
> COMPRAR pan
5c5
< Regar las plantas
---
> Regar   las plantas
$ diff -i tareas.txt /tmp/t3.txt             # sin contar las mayúsculas
5c5
< Regar las plantas
---
> Regar   las plantas
$ diff -i -b tareas.txt /tmp/t3.txt          # ni los espacios repetidos: iguales
$ diff -q tareas.txt /tmp/t3.txt
Files tareas.txt and /tmp/t3.txt differ

-q, de quiet, reduce todo a una línea. Es lo que haría cmp, con una diferencia: diff -q acepta las opciones de esta sección. «¿Son iguales, sin contar los espacios?» solo puede responderlo diff -qw.

8. Comparar directorios

Si los dos argumentos son directorios, diff compara los archivos que tienen el mismo nombre en uno y en otro, e informa cuáles existen en uno solo. Con -r desciende además por los subdirectorios. Armamos dos directorios pequeños, como si fueran dos versiones de un proyecto:

terminal
$ mkdir /tmp/v1 /tmp/v2
$ cp tareas.txt ventas.csv /tmp/v1
$ cp ventas.csv /tmp/objetivos.csv /tmp/v2
$ cp /tmp/tareas2.txt /tmp/v2/tareas.txt     # mismo nombre, otro contenido
$ diff -rq /tmp/v1 /tmp/v2
Only in /tmp/v2: objetivos.csv
Files /tmp/v1/tareas.txt and /tmp/v2/tareas.txt differ

La combinación -rq da el resumen: una línea por cada archivo que sobra, que falta o que cambió. ventas.csv, idéntico en los dos, no se menciona. Es la primera pregunta ante dos copias de un proyecto, o ante un respaldo y su original: ¿en qué archivos hay diferencias? Sin la -q, a continuación de cada pareja distinta aparece su comparación completa:

terminal
$ diff -r /tmp/v1 /tmp/v2
Only in /tmp/v2: objetivos.csv
diff -r /tmp/v1/tareas.txt /tmp/v2/tareas.txt
2d1
< Llamar a Luis
4c3
< Estudiar vim
---
> Estudiar awk
5a5
> Sacar la basura

Es la comprobación que hicimos en el tema 15 después de extraer un archivo tar: diff -r entre el original y lo extraído, sin salida, certificaba que el respaldo estaba completo. Se combina con -u para el detalle en formato unificado, y con --exclude='*.log' para dejar fuera lo que no interesa comparar.

9. patch: aplicar las diferencias

La salida de diff es, dijimos, una receta para transformar un archivo en otro. Es una receta que se puede ejecutar. El programa que lo hace es patch, y el archivo con las diferencias se llama, por eso, parche.

terminal
$ diff -u tareas.txt /tmp/tareas2.txt > /tmp/cambios.patch    # guardar las diferencias
$ cp tareas.txt /tmp/t.txt                                    # una copia del original
$ patch /tmp/t.txt < /tmp/cambios.patch                       # aplicarle los cambios
patching file /tmp/t.txt
$ cmp /tmp/t.txt /tmp/tareas2.txt                             # sin salida: quedó igual al nuevo
$ patch -R /tmp/t.txt < /tmp/cambios.patch                    # deshacer
patching file /tmp/t.txt
$ cmp /tmp/t.txt tareas.txt                                   # sin salida: volvió al original

La copia del archivo viejo, más el parche, dio exactamente el archivo nuevo. Y con -R, de reverse, el mismo parche se aplicó al revés. A diferencia de las herramientas del tema, patch sí modifica el archivo que recibe.

La idea tiene más alcance del que parece. Quien corrige tres líneas en un programa de cien mil no necesita enviar el programa: envía el parche, unos pocos renglones que cualquiera puede leer antes de aceptar. Las líneas de contexto del formato unificado le permiten a patch encontrar el lugar correcto aunque el archivo de destino haya cambiado un poco. Así se desarrolló el software libre durante décadas, con parches enviados por correo, y sobre esa misma idea —guardar los cambios, no las copias— están construidos los sistemas de control de versiones como Git.

10. comm: comparar dos listas

Cambiamos de clase de archivo. Las dos listas de productos de la sección 2 no son textos donde importe el renglón: son conjuntos. La pregunta ya no es «qué línea cambió», sino «qué elementos tienen en común, y cuáles son de una sola».

comm compara dos archivos ordenados línea por línea, y reparte las líneas en tres columnas: las que están solo en el primero, las que están solo en el segundo, y las comunes a los dos.

terminal
$ paste /tmp/rosario.txt /tmp/mendoza.txt    # las dos listas, lado a lado
cable	monitor
monitor	mouse
teclado	
$ comm /tmp/rosario.txt /tmp/mendoza.txt
cable
		monitor
	mouse
teclado

La salida es una sola lista, con todos los productos en orden, y lo que informa es la sangría de cada uno. Sin sangría, primera columna: solo se vende en Rosario. Con una tabulación, segunda columna: solo en Mendoza. Con dos, tercera columna: en las dos ciudades.

Las tres columnas de comm al comparar los productos de Rosario y de Mendoza: en la columna 1, cable y teclado, que están solo en el primer archivo; en la columna 3, monitor, que está en los dos; en la columna 2, mouse, que está solo en el segundo solo en el primero Columna 1 cable teclado en los dos Columna 3 monitor solo en el segundo Columna 2 mouse comm -23 comm -12 comm -13
Las tres columnas de comm, y la opción que deja ver cada una. La tercera, la de las líneas comunes, es la que da nombre al comando.

Casi nunca se quieren las tres columnas juntas. Las opciones -1, -2 y -3 hacen algo que al principio se lee al revés: suprimen la columna indicada. Para ver una, se quitan las otras dos:

ComandoDeja verEn términos de conjuntos
comm -12 a bLas líneas que están en los dos.La intersección.
comm -23 a bLas que están en a y no en b.La diferencia, a menos b.
comm -13 a bLas que están en b y no en a.La diferencia, b menos a.
comm -3 a bLas que están en uno solo de los dos.La diferencia simétrica.
terminal
$ comm -12 /tmp/rosario.txt /tmp/mendoza.txt     # se vende en las dos ciudades
monitor
$ comm -23 /tmp/rosario.txt /tmp/mendoza.txt     # en Rosario, y no en Mendoza
cable
teclado
$ comm -13 /tmp/rosario.txt /tmp/mendoza.txt     # en Mendoza, y no en Rosario
mouse

Con una sola columna a la vista no queda sangría, y la salida es una lista limpia, apta para seguir en una tubería. Es la respuesta directa a la tercera pregunta con que abrimos el tema: con los inscriptos en un archivo y los que pagaron en otro, comm -23 inscriptos.txt pagaron.txt entrega los que deben.

comm solo funciona con archivos ordenados

comm avanza por los dos archivos a la vez, como quien coteja dos listas alfabéticas con un dedo en cada una. Si alguna no está ordenada, se desacomoda: avisa con comm: file 1 is not in sorted order y el resultado no sirve. Tienen que estar ordenados con sort, sin opciones —el orden alfabético, no el numérico de -n—, y sin líneas repetidas si se los quiere tratar como conjuntos: sort -u hace las dos cosas.

11. join: cruzar dos tablas

La última herramienta no compara: combina. Tenemos dos tablas que hablan de las mismas personas. Una dice cuánto vendió cada vendedor; la otra, cuánto debía vender. Para saber quién cumplió hay que poner los dos datos en un mismo renglón.

join une las líneas de dos archivos que tienen el mismo valor en un campo, llamado clave. Por cada pareja escribe una línea con la clave, los demás campos del primer archivo y los demás campos del segundo.

terminal
$ join -t, /tmp/totales.csv /tmp/objetivos.csv
Ana,731,600
Luis,495,500
Marta,395,350
cómo se formó cada línea
totales.csv        objetivos.csv        resultado

Ana,731       +    Ana,600         →    Ana,731,600
Luis,495      +    Luis,500        →    Luis,495,500
Marta,395     +    Marta,350       →    Marta,395,350
                   Pedro,300            (sin pareja: no aparece)

La opción -t indica el separador de campos, como en sort; sin ella, join separa por espacios. Si no se dice otra cosa, la clave es el primer campo de cada archivo. Pedro está en un solo archivo, no tiene con quién unirse, y queda fuera.

No hay que confundir esto con paste, del tema 26, que también junta dos archivos. paste une la línea 1 con la línea 1 y la 2 con la 2, por su posición, sin mirar qué dicen. join une por contenido: busca en el otro archivo la línea que tiene la misma clave. Si a uno de los archivos le faltara una fila, paste desalinearía todo lo que sigue; join no.

El resultado es un archivo de campos como cualquier otro, y se lo puede seguir procesando. Con el awk del tema anterior, la respuesta a la pregunta:

terminal: cuánto le sobró o le faltó a cada uno
$ join -t, /tmp/totales.csv /tmp/objetivos.csv | awk -F, '{print $1, $2 - $3}'
Ana 131
Luis -5
Marta 45

Ana y Marta superaron su objetivo; a Luis le faltaron cinco pesos. Quien haya trabajado con bases de datos habrá reconocido la operación: es el JOIN de SQL, y también el BUSCARV de las planillas de cálculo, hecho con archivos de texto.

join también exige orden, y por el campo clave

Igual que comm, join recorre los dos archivos en paralelo. Los dos deben estar ordenados por el campo que se usa como clave. Si no lo están, avisa —join: ventas.csv:3: is not sorted— y deja parejas sin unir. Ordenar por la clave, y no por la línea entera, se pide con sort -t, -k2,2 cuando la clave es el segundo campo.

12. join: columnas, y filas sin pareja

Dos cosas quedaron fijas en el ejemplo anterior y se pueden ajustar: cuál es el campo clave de cada archivo, y qué pasa con las líneas que no encuentran pareja.

OpciónEfecto
-t,El separador de campos, de entrada y de salida.
-1 NLa clave del primer archivo es el campo N.
-2 NLa clave del segundo archivo es el campo N.
-a 1, -a 2Incluye además las líneas sin pareja del archivo 1, o del 2.
-v 1, -v 2Muestra solo las líneas sin pareja de ese archivo.
-o listaElige qué campos escribir, y en qué orden.
-e textoLo que se escribe en lugar de un campo que falta. Se usa con -o.
-iNo distingue mayúsculas de minúsculas en la clave.
terminal: las filas sin pareja
$ join -t, -v 2 /tmp/totales.csv /tmp/objetivos.csv      # tienen objetivo y no tienen ventas
Pedro,300
$ join -t, -a 2 /tmp/totales.csv /tmp/objetivos.csv      # todos los del segundo archivo
Ana,731,600
Luis,495,500
Marta,395,350
Pedro,300

Con -a 2 Pedro aparece, pero su línea tiene dos campos donde las demás tienen tres, y el 300 quedó en la columna de las ventas. Para que la tabla salga pareja hay que decir qué campos se quieren, con -o, y con qué rellenar los que falten, con -e:

terminal
$ join -t, -a 2 -e 0 -o 0,1.2,2.2 /tmp/totales.csv /tmp/objetivos.csv
Ana,731,600
Luis,495,500
Marta,395,350
Pedro,0,300

En la lista de -o, cada campo se nombra con el número del archivo, un punto y el número del campo: 1.2 es el segundo campo del primer archivo. El 0 representa la clave, venga del archivo que venga.

Las opciones -1 y -2 permiten cruzar archivos donde la clave no es el primer campo. En ventas.csv el vendedor es el segundo, y el archivo está ordenado por fecha. Hay que ordenarlo antes por ese campo; lo hacemos al paso, con la sustitución de procesos del tema 25:

terminal: agregar a cada venta el objetivo de su vendedor
$ join -t, -1 2 -2 1 <(sort -t, -k2,2 ventas.csv) /tmp/objetivos.csv | head -n 4
Ana,2026-10-02,Salta,mouse,5,75,600
Ana,2026-10-03,Mendoza,monitor,2,640,600
Ana,2026-10-05,Rosario,cable,4,16,600
Luis,2026-10-01,Rosario,teclado,2,90,500
$ join -t, -1 2 -2 1 -o 1.1,0,1.6,2.2 <(sort -t, -k2,2 ventas.csv) /tmp/objetivos.csv | head -n 3
2026-10-02,Ana,75,600
2026-10-03,Ana,640,600
2026-10-05,Ana,16,600

Dos cosas para observar. La clave se escribe primero, aunque en el archivo original fuera el segundo campo; con -o se la vuelve a poner donde se quiera. Y la línea de objetivos de Ana se unió con sus tres ventas: cuando una clave se repite en un archivo, se forman todas las parejas posibles.

13. Comparar salidas de comandos

En este tema todo fueron archivos, y sin embargo lo que más se quiere comparar a menudo no está en ningún archivo: son las salidas de dos comandos. Una tubería no alcanza, porque estas herramientas esperan dos nombres de archivo. El recurso es el que acabamos de usar, y que en el tema 25 presentamos justamente con diff:

La sustitución de procesos, <(comando), ejecuta el comando y pone en su lugar un nombre de archivo del que se puede leer su salida.

terminal
$ diff <(ls /tmp/v1) <(ls /tmp/v2)           # qué nombres hay en un directorio y no en el otro
0a1
> objetivos.csv
$ comm -13 <(cut -d, -f2 ventas.csv | sort -u) <(cut -d, -f1 /tmp/objetivos.csv)
Pedro

El segundo comando responde «¿qué vendedores tienen objetivo y ninguna venta?» sin haber creado ningún archivo intermedio: cada lista se arma, se ordena y se entrega en el momento. Es la forma en que comm y join se usan casi siempre, porque resuelve de paso la exigencia del orden: el sort va adentro.

Quiero comparar…Comando
Un archivo antes y después de un filtrodiff archivo <(sed 's/a/b/' archivo)
Dos archivos sin importar el orden de sus líneasdiff <(sort a) <(sort b)
El contenido de dos directorios, solo los nombresdiff <(ls dir1) <(ls dir2)
Una configuración sin comentarios con otradiff <(grep -v '^#' a) <(grep -v '^#' b)
Las variables de entorno de dos momentosenv | sort > /tmp/antes; luego diff /tmp/antes <(env | sort)

La primera fila es una manera de practicar el procedimiento prudente del tema 29: ver exactamente qué líneas cambiaría una orden de sed antes de aplicarla con -i. Y cuando diff no tiene más que un archivo y una tubería, acepta un guion en lugar de un nombre para leer de su entrada estándar: sort a | diff - b.

14. Recetas y errores comunes

Quiero…Comando
Saber si dos archivos son idénticoscmp a b
Lo mismo, en un scriptcmp -s a b && echo iguales
Ver qué cambió entre dos versionesdiff -u viejo nuevo
Verlo en dos columnasdiff -y viejo nuevo | less
Compararlos sin contar espacios ni mayúsculasdiff -iw a b
Saber en qué archivos difieren dos directoriosdiff -rq dir1 dir2
Guardar los cambios, y aplicarlos a otra copiadiff -u viejo nuevo > c.patch; patch copia < c.patch
Las líneas comunes a dos listascomm -12 <(sort a) <(sort b)
Las que están en la primera y faltan en la segundacomm -23 <(sort a) <(sort b)
Cruzar dos CSV por su primer campojoin -t, <(sort a.csv) <(sort b.csv)
Las filas de una tabla sin correspondencia en la otrajoin -t, -v 1 a.csv b.csv
Comprobar la integridad de una descargasha256sum archivo
SíntomaCausaSolución
Lo agregado aparece como quitado.Los argumentos están invertidos.Primero el viejo, después el nuevo.
diff marca todas las líneas, y se ven iguales.Un archivo tiene finales de línea de Windows.diff --strip-trailing-cr
Marca líneas que parecen idénticas.Espacios al final, o tabulaciones en lugar de espacios.diff -w; verlas con cat -A.
\ No newline at end of fileLa última línea de un archivo no termina con salto de línea.Es un aviso, no un error.
Binary files a and b differNo son archivos de texto.Para ellos, cmp.
comm: file 1 is not in sorted orderEl archivo no está ordenado.comm <(sort a) <(sort b)
comm no encuentra líneas comunes que sí existen.Difieren en un espacio final, o en mayúsculas.Normalizar antes, con sed o tr.
join no escribe nada.Falta -t: toda la línea se toma como clave.join -t,
join: … is not sortedNo está ordenado por el campo clave, o se ordenó con sort -n.sort -t, -k2,2, sin -n.
Faltan filas en el resultado de join.No tienen pareja en el otro archivo.-a 1 o -a 2.
patch responde Hunk #1 FAILED.El archivo no es la versión a partir de la cual se hizo el parche.Aplicarlo sobre el archivo original.

La referencia del tema:

Se escribeQué hace
cmp a bDice si son idénticos, byte por byte.
diff a bMuestra las líneas que cambian de a a b.
diff -u a bLo mismo, en formato unificado, con contexto.
diff -y a bLado a lado.
diff -q a bSolo dice si difieren.
diff -i, -b, -w, -BIgnora mayúsculas; cantidad de espacios; todos los espacios; líneas vacías.
diff -r dir1 dir2Compara dos directorios completos.
patch archivo < cambios.patchAplica las diferencias guardadas.
comm a bTres columnas: solo en a, solo en b, en los dos.
comm -12, -23, -13Las comunes; las exclusivas de a; las exclusivas de b.
join -t, a bUne las líneas con el mismo primer campo.
join -1 N -2 M, -a, -v, -oElige las claves; incluye o aísla las filas sin pareja; elige las columnas.
<(comando)La salida de un comando, usada como archivo.

Los archivos de /tmp pueden quedar; el directorio de práctica no se modificó. Con este tema termina el bloque dedicado al texto: buscar con grep, describir formas con expresiones regulares, editar con sed, calcular con awk, y ahora comparar y cruzar. Hasta aquí la terminal fue una manera de trabajar con archivos. En el tema 32 empezamos a mirar lo que está vivo en el sistema: los programas en ejecución, es decir, los procesos.

15. Resumen

16. Preguntas de repaso

Intenta responder cada una antes de abrir la respuesta.

1. ¿Qué significa que cmp a b no escriba nada?

Que los dos archivos son idénticos, byte por byte.

2. ¿En qué se diferencian cmp y diff?

cmp compara bytes, sirve para cualquier archivo y solo informa dónde está la primera diferencia. diff compara líneas de texto y muestra todos los cambios.

3. ¿Qué códigos de salida devuelve diff?

0 si los archivos son iguales, 1 si son distintos y 2 si hubo un error.

4. En la salida normal de diff a b, ¿qué indican < y >?

< precede a las líneas del primer archivo, a; >, a las del segundo, b.

5. ¿Cómo se lee 3d2? ¿Y 7a8,9?

3d2: la línea 3 del primer archivo no está en el segundo; habría quedado después de la 2. 7a8,9: después de la línea 7 del primero, el segundo tiene dos líneas más, la 8 y la 9.

6. ¿Qué cambia en la salida si se invierten los argumentos de diff?

La dirección de los cambios: lo que aparecía como agregado aparece como borrado, y a la inversa.

7. En el formato unificado, ¿qué significan las líneas que empiezan con espacio, con - y con +?

Con espacio, líneas de contexto, iguales en los dos. Con -, líneas que están solo en el archivo viejo. Con +, líneas que están solo en el nuevo.

8. ¿Cómo se lee la marca @@ -10,6 +10,7 @@?

El bloque abarca seis líneas del archivo viejo a partir de la 10, y siete del nuevo a partir de la 10: se agregó una línea.

9. ¿Qué diferencia hay entre diff -b y diff -w?

-b ignora los cambios en la cantidad de espacios, pero sigue distinguiendo «a b» de «ab». -w ignora todos los espacios.

10. ¿Qué muestra diff -rq dir1 dir2?

Una línea por cada archivo que existe en un solo directorio, y una por cada pareja de archivos del mismo nombre con contenido distinto, recorriendo los subdirectorios.

11. ¿Qué hace patch archivo < cambios.patch?

Modifica archivo aplicándole las diferencias guardadas en cambios.patch, que es la salida de un diff.

12. ¿Qué contienen las tres columnas de comm a b?

La primera, las líneas que están solo en a; la segunda, las que están solo en b; la tercera, las que están en los dos.

13. Escribe el comando que muestra las líneas de a que no están en b, sin suponer que están ordenados.

comm -23 <(sort a) <(sort b)

14. ¿En qué se diferencian paste a b y join a b?

paste une las líneas por su posición: la primera con la primera. join las une por contenido: las que tienen el mismo valor en el campo clave.

15. Un join devuelve menos filas que las que tiene el primer archivo. ¿Por qué, y cómo se ven las que faltan?

Porque las filas sin pareja en el otro archivo se omiten. join -v 1 a b muestra solo esas, y join -a 1 a b las incluye en el resultado.

Para practicar

  1. Ejecuta diff /tmp/tareas2.txt tareas.txt, con los argumentos invertidos. Antes, escribe en un papel la salida que esperas.
  2. Copia tareas.txt a /tmp, modifícalo con nano —borra una línea, cambia otra, agrega dos— y compáralo con el original en los tres formatos: normal, -u y -y.
  3. Obtén con sed una versión de ventas.csv donde Salta sea SALTA. Compárala con el original con diff, con diff -i y con diff -q. ¿Cuántas líneas cambiaron?
  4. Compara ventas.csv con sort ventas.csv usando la sustitución de procesos. Luego ordénalo por importe y compara de nuevo. ¿Por qué la segunda salida es tanto más larga?
  5. Con una copia de informe.txt en /tmp, cambia tcp por TCP con sed -i.bak, genera un parche con diff -u entre el respaldo y el resultado, y aplícalo con patch a otra copia del original. Verifica con cmp.
  6. Copia ~/practica a /tmp/practica2, borra allí un archivo, modifica otro y crea uno nuevo. Encuentra los tres cambios con diff -rq.
  7. Con comm y la sustitución de procesos, averigua qué productos vendió Luis que no vendió Ana, cuáles vendieron los dos, y qué ciudades visitó Marta que no visitó Luis.
  8. Averigua con comm qué comandos están en /usr/bin y no en /usr/sbin, y cuántos nombres comparten.
  9. Crea /tmp/precios.csv con el precio de lista de cada producto (cable,5, monitor,350…). Crúzalo con ventas.csv por el producto, y con awk muestra las ventas hechas por debajo del precio de lista.
  10. Prueba join /tmp/totales.csv /tmp/objetivos.csv, sin -t, y comm tareas.txt /tmp/tareas2.txt. Explica los dos resultados.