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.


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


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)


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.


¡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

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í

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.