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:
| Herramienta | Responde a | Mira los archivos como | Exige 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:
$ 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:
$ 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.
$ 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ón | Efecto |
|---|---|
| Ninguna | Informa la primera diferencia, o no escribe nada. |
-s | Silencioso: no escribe nunca. El resultado se lee en el código de salida. |
-l | Lista 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ódigo | Significa |
|---|---|
0 | Los archivos son iguales. |
1 | Son distintos. |
2 | Hubo un error: un archivo no existe o no se puede leer. |
$ 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.
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.
$ 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.
4c3
│││
││└─ la línea correspondiente en el segundo archivo
│└─ la operación: c, cambiar
└─ la línea del primer archivo
| Letra | Viene de | Significa | Muestra |
|---|---|---|---|
a | add | En el segundo archivo hay líneas que el primero no tiene. | Solo líneas con >. |
d | delete | En el primero hay líneas que el segundo no tiene. | Solo líneas con <. |
c | change | Unas 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:
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.
$ 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 con | Significa |
|---|---|
--- 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 espacio | Una 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 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:
$ 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.
| Formato | Opción | Conviene para |
|---|---|---|
| Normal | Ninguna | Cambios pequeños; es el que aparece por defecto. |
| Unificado | -u | Leer, guardar y compartir cambios. La elección general. |
| Lado a lado | -y | Recorrer con la vista dos archivos de líneas cortas. |
| Solo un aviso | -q | Saber 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ón | Ignora |
|---|---|
-i | Las diferencias entre mayúsculas y minúsculas. |
-b | Los cambios en la cantidad de espacios: uno o varios da igual. |
-w | Todos los espacios, incluso si en un archivo los hay y en el otro no. |
-B | Las líneas vacías agregadas o quitadas. |
--strip-trailing-cr | El retorno de carro de los finales de línea de Windows. |
$ 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:
$ 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:
$ 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.
$ 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.
$ 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.
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:
| Comando | Deja ver | En términos de conjuntos |
|---|---|---|
comm -12 a b | Las líneas que están en los dos. | La intersección. |
comm -23 a b | Las que están en a y no en b. | La diferencia, a menos b. |
comm -13 a b | Las que están en b y no en a. | La diferencia, b menos a. |
comm -3 a b | Las que están en uno solo de los dos. | La diferencia simétrica. |
$ 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 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.
$ join -t, /tmp/totales.csv /tmp/objetivos.csv
Ana,731,600
Luis,495,500
Marta,395,350
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:
$ 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.
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ón | Efecto |
|---|---|
-t, | El separador de campos, de entrada y de salida. |
-1 N | La clave del primer archivo es el campo N. |
-2 N | La clave del segundo archivo es el campo N. |
-a 1, -a 2 | Incluye además las líneas sin pareja del archivo 1, o del 2. |
-v 1, -v 2 | Muestra solo las líneas sin pareja de ese archivo. |
-o lista | Elige qué campos escribir, y en qué orden. |
-e texto | Lo que se escribe en lugar de un campo que falta. Se usa con -o. |
-i | No distingue mayúsculas de minúsculas en la clave. |
$ 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:
$ 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:
$ 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.
$ 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 filtro | diff archivo <(sed 's/a/b/' archivo) |
| Dos archivos sin importar el orden de sus líneas | diff <(sort a) <(sort b) |
| El contenido de dos directorios, solo los nombres | diff <(ls dir1) <(ls dir2) |
| Una configuración sin comentarios con otra | diff <(grep -v '^#' a) <(grep -v '^#' b) |
| Las variables de entorno de dos momentos | env | 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énticos | cmp a b |
| Lo mismo, en un script | cmp -s a b && echo iguales |
| Ver qué cambió entre dos versiones | diff -u viejo nuevo |
| Verlo en dos columnas | diff -y viejo nuevo | less |
| Compararlos sin contar espacios ni mayúsculas | diff -iw a b |
| Saber en qué archivos difieren dos directorios | diff -rq dir1 dir2 |
| Guardar los cambios, y aplicarlos a otra copia | diff -u viejo nuevo > c.patch; patch copia < c.patch |
| Las líneas comunes a dos listas | comm -12 <(sort a) <(sort b) |
| Las que están en la primera y faltan en la segunda | comm -23 <(sort a) <(sort b) |
| Cruzar dos CSV por su primer campo | join -t, <(sort a.csv) <(sort b.csv) |
| Las filas de una tabla sin correspondencia en la otra | join -t, -v 1 a.csv b.csv |
| Comprobar la integridad de una descarga | sha256sum archivo |
| Síntoma | Causa | Solució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 file | La última línea de un archivo no termina con salto de línea. | Es un aviso, no un error. |
Binary files a and b differ | No son archivos de texto. | Para ellos, cmp. |
comm: file 1 is not in sorted order | El 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 sorted | No 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 escribe | Qué hace |
|---|---|
cmp a b | Dice si son idénticos, byte por byte. |
diff a b | Muestra las líneas que cambian de a a b. |
diff -u a b | Lo mismo, en formato unificado, con contexto. |
diff -y a b | Lado a lado. |
diff -q a b | Solo dice si difieren. |
diff -i, -b, -w, -B | Ignora mayúsculas; cantidad de espacios; todos los espacios; líneas vacías. |
diff -r dir1 dir2 | Compara dos directorios completos. |
patch archivo < cambios.patch | Aplica las diferencias guardadas. |
comm a b | Tres columnas: solo en a, solo en b, en los dos. |
comm -12, -23, -13 | Las comunes; las exclusivas de a; las exclusivas de b. |
join -t, a b | Une las líneas con el mismo primer campo. |
join -1 N -2 M, -a, -v, -o | Elige 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
- Para comparar hay que saber qué se tiene:
diffes para textos, donde importa la posición de cada línea;comm, para listas, donde no importa. cmpdice si dos archivos son idénticos, byte por byte, y sirve para cualquier tipo de archivo. Si son iguales no escribe nada.cmpydiffterminan con código 0 si los archivos son iguales, 1 si difieren y 2 si hubo un error.diff viejo nuevomuestra los cambios mínimos para pasar del primero al segundo. El orden de los argumentos importa.- En el formato normal,
<marca líneas del primer archivo y>del segundo; las órdenesa,dycindican agregado, borrado y cambio. diff -uproduce el formato unificado:-para lo quitado,+para lo agregado, y líneas de contexto. Es el que usan Git y los parches.diff -ymuestra los archivos lado a lado;-i,-b,-wy-Bignoran mayúsculas, espacios y líneas vacías;-qsolo avisa.diff -rcompara dos directorios; con-q, lista qué archivos difieren o faltan.patchaplica a un archivo las diferencias guardadas pordiff, y con-Rlas deshace.commsepara las líneas de dos archivos ordenados en tres columnas;-12deja las comunes,-23y-13las exclusivas de cada uno.joinune las líneas de dos archivos que tienen la misma clave. Con-tse da el separador; con-1y-2, el campo clave; con-ay-v, las filas sin pareja.commyjoinexigen archivos ordenados consort;join, por el campo clave.- Con
<(comando)se comparan salidas de comandos sin crear archivos intermedios.
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
- Ejecuta
diff /tmp/tareas2.txt tareas.txt, con los argumentos invertidos. Antes, escribe en un papel la salida que esperas. - Copia
tareas.txta/tmp, modifícalo connano—borra una línea, cambia otra, agrega dos— y compáralo con el original en los tres formatos: normal,-uy-y. - Obtén con
seduna versión deventas.csvdondeSaltaseaSALTA. Compárala con el original condiff, condiff -iy condiff -q. ¿Cuántas líneas cambiaron? - Compara
ventas.csvconsort ventas.csvusando la sustitución de procesos. Luego ordénalo por importe y compara de nuevo. ¿Por qué la segunda salida es tanto más larga? - Con una copia de
informe.txten/tmp, cambiatcpporTCPconsed -i.bak, genera un parche condiff -uentre el respaldo y el resultado, y aplícalo conpatcha otra copia del original. Verifica concmp. - Copia
~/practicaa/tmp/practica2, borra allí un archivo, modifica otro y crea uno nuevo. Encuentra los tres cambios condiff -rq. - Con
commy 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. - Averigua con
commqué comandos están en/usr/biny no en/usr/sbin, y cuántos nombres comparten. - Crea
/tmp/precios.csvcon el precio de lista de cada producto (cable,5,monitor,350…). Crúzalo conventas.csvpor el producto, y conawkmuestra las ventas hechas por debajo del precio de lista. - Prueba
join /tmp/totales.csv /tmp/objetivos.csv, sin-t, ycomm tareas.txt /tmp/tareas2.txt. Explica los dos resultados.