Diferencia de cuerdas en Bash

110

Estoy tratando de encontrar una manera de determinar la diferencia entre dos cadenas en mi script. Podría hacer esto fácilmente con diff o comm, pero no estoy tratando con archivos y prefiero no enviarlos a archivos, hacer la comparación y volver a leerlos.

Veo que comm, diff, cmp permiten pasar dos archivos O un archivo y una entrada estándar; supongo que eso es bueno si no quiero generar dos archivos ... pero todavía es un poco horrible.

He estado investigando pensando que puedo usar grep o expresiones regulares, pero supongo que no.

codeforester
fuente
1
¿Qué es lo que realmente quieres hacer?
Puede usar manipulaciones de subcadenas y operaciones de prueba integradas con cambios IFS para comparar, pero necesitaría saber si desea comparar carácter por carácter, palabra por palabra, línea por línea, ignorar los espacios en blanco ...
technosaurus

Respuestas:

198

Usando diffo como lo que quieras:

diff  <(echo "$string1" ) <(echo "$string2")

Preguntas frecuentes de Greg's Bash: Sustitución de procesos

o con una tubería con nombre

mkfifo ./p
diff - p <<< "$string1" & echo "$string2" > p

Preguntas frecuentes de Greg's Bash: Trabajar con canalizaciones con nombre

La tubería con nombre también se conoce como FIFO.

El -solo es para entrada estándar.

<<< es una "cadena aquí".

&es como ;pero lo pone en segundo plano

Ian Kelling
fuente
5
+1 para respuesta correcta. +1 para una gran explicación de los símbolos. Además, Greg's Bash FAQ se ha trasladado a: mywiki.wooledge.org Los enlaces para las páginas anteriores están ahora en mywiki.wooledge.org/ProcessSubstitution y mywiki.wooledge.org/BashFAQ/085
timemachine3030
¡Gracias! y también, esto mostrará los descriptores de archivos dinámicosFUNC(){ echo "$@"; "$@"; }; FUNC diff <(echo a) <(echo b);
Aquarius Power
Estaba buscando eso para comparar dos shasums. No estoy seguro de si hay una forma más elegante de hacerlo, pero funciona.
fuma
Esto parece funcionar si hay varias líneas en $ string1 y $ string2, y diff genera las líneas que se agregaron o restaron. ¿Qué pasa si la cadena es una sola línea y una línea y hay alguna diferencia entre las dos cadenas?
alpha_989
@ alpha_989, aquí está su respuesta: $ diff <(echo "Here are the letters in String One.") <(echo "Here are the characters in String Two.") \n 1c1 \n < Here are the letters in String One. \n --- \n > Here are the characters in String Two. \nusar la tubería es similar, excepto que muestra un número de proceso, comienza 1c1después del siguiente $y espera hasta que presione <kbd> Enter <kbd> (o puede hacer otros comandos ...)
bballdave025
19

Me recuerda esta pregunta: ¿Cómo se pueden diferenciar dos pipelines en Bash?

Si está en una sesión de bash, puede hacer lo siguiente:

diff <cmd1 <cmd2
diff <(foo | bar) <(baz | quux)

con la <creación de canalizaciones con nombre anónimo, administradas por bash, para que se creen y destruyan automáticamente, a diferencia de los archivos temporales.

Entonces, si logra aislar sus dos cadenas diferentes como parte de un comando (grep, awk, sed, ...), puede hacer, por ejemplo, algo como:

diff < grep string1 myFile < grep string2 myFile

(si supone que tiene en su archivo líneas como string1=very_complicated_valuey un string2=another_long_and_complicated_value': sin conocer el formato interno de su archivo, no puedo recomendar un comando preciso)

VonC
fuente
13

Prefiero una cmpfunción de sustitución de proceso de bash:

$ cmp -bl <(echo -n abcda) <(echo -n aqcde)
  2 142 b    161 q
  5 141 a    145 e

Decir sobre la posición 2, ab ocurre para el primero, pero aq para el segundo. En la posición 5, está sucediendo otra diferencia. Simplemente reemplace esas cadenas por variables y listo.

Johannes Schaub - litb
fuente
¡Esto solo funciona cuando las cadenas tienen la misma longitud!
strpeter
11

Digamos que tienes tres cuerdas

a="this is a line"
b="this is"
c="a line"

Para eliminar el prefijo b de a

echo ${a#"$b"}  # a line

Para eliminar el sufijo c de un

echo ${a%"$c"}  # this is
Pithikos
fuente
2
Supongo que esta es la forma bash de hacerlo. Funcionó muy bien. Sin embargo, esa sintaxis es un poco difícil de entender.
Mikael Roos
@MikaelRoos De acuerdo. Más fácil de leer (para mí de todos modos) sería usar sed: echo "$a" | sed "s!^$b!!g" (cambié el separador sed estándar / por! En caso de que las variables que se están tratando sean rutas. Además, podría usar una cadena aquí en lugar de echo:. sed ... <<< $a)
ACK_stoverflow
0

Otro ejemplo:

before="184613 102050 83756 63054"
after="184613 102050 84192 83756 63054"

comm -23 <(tr ' ' $'\n' <<< $after | sort) <(tr ' ' $'\n' <<< $before | sort)

Salidas

84192

Respuesta original aquí

Sida Zhou
fuente