Compare 2 números y copie solo la parte similar sed/grep/awk

Compare 2 números y copie solo la parte similar sed/grep/awk

Suponiendo que tengo una matriz llamada a. Hay 2 entradas en una matriz a[1]y a[2], por lo tanto, cada elemento contiene un valor numérico. Ambos valores tienen números iniciales similares, pero tienen finales diferentes. Debo copiar la parte similar e ignorar el resto.

Entonces como unejemplo

$ echo ${a[1]}
.1.3.6.1.4.1.232.13600256

$ echo ${a[2]}
.1.3.6.1.4.1.232.13600276

Necesito algún comando para comparar estos elementos y luego copiar solo la parte similarhasta el primer campo no coincidente. es decir, en este ejemplo

PRODUCCIÓN

similar part is .1.3.6.1.4.1.232

Otro ejemplo

$ echo ${a[1]}
.1.3.6.1.4.1.759.2344.454545

$ echo ${a[2]}
.1.3.6.1.4.1.759.3234.454545

SALIDA para este ejemplo

similar part is .1.3.6.1.4.1.759

Respuesta1

DeDesbordamiento de pila:

En sed, suponiendo que las cadenas no contengan caracteres de nueva línea:

string1="test toast"
string2="test test"
printf "%s\n%s\n" "$string1" "$string2" | sed -e 'N;s/^\(.*\).*\n\1.*$/\1/'

Esto supone que las cadenas en sí no contienen nuevas líneas.

Por lo tanto puedes hacer:

printf "%s\n" "${a[1]}" "${a[2]}" | sed -r 'N;s/^(.*)(\..*)?\n\1.*$/\1/'

El(\..*) deberíaeliminar un final .de la sección común.


La solución consta de dos partes:

  • Ponerse seda trabajar en dos líneas. Esto se hace usando Ny se puede evitar si se garantiza que un carácter no estará en la entrada. Por ejemplo, debido a que los espacios no están presentes en los elementos tal como se proporcionan, podemos usar:

    printf "%s " "${a[1]}" "${a[2]}" | sed -r 's/^(.*)(\..*)? \1.*$/\1/'
    

    Básicamente, el carácter o cadena que separa los dos elementos en la salida debe usarse después %sen la printfcadena de formato y antes \1en la expresión regular.

  • Encontrar una cadena repetida usando expresiones regulares. El truco para esto es bien conocido y siempre es una variación de:

    (.*)\1
    

    .*coincide con cualquier conjunto de caracteres y ()los agrupa para referencia posterior, por \1. Así (.*)\1, cualquier secuencia de caracteres sigue a sí misma.

Respuesta2

Esta es una forma de Perl. La idea es dividir ambas cadenas de entrada en matrices separadas e iterar sobre las matrices, guardando las entradas que sean idénticas en ambas:

perl -le '@A=split(//,$ARGV[0]);@B=split(//,$ARGV[1]); 
          for $i (0..$#A){$A[$i] eq $B[$i] ? push @S,$A[$i] : last} 
          print @S' "${a[0]}" "${a[1]}"
.1.3.6.1.4.1.759.

Eso, sin embargo, incluye el seguimiento .. Su salida no lo hace (a pesar de ser la misma en ambas variables), así que si desea eliminarla, use esto en su lugar:

$ perl -le '@A=split(/\./,$ARGV[0]);@B=split(/\./,$ARGV[1]); 
            for $i (0..$#A){$A[$i] eq $B[$i] ? push @S,$A[$i] : last} 
            print join ".",@S' "${a[0]}" "${a[1]}"
.1.3.6.1.4.1.759

Explicación

  • -le: agregar un nuevoyoIngrese a cada llamada de printy ejecute el script proporcionado por -e.
  • @A=split(//,$ARGV[0]): $ARGV[0]es el primer argumento dado en la línea de comando. Esto lo dividirá, convirtiendo a cada carácter en un elemento de la matriz @A.
  • @B=split(//,$ARGV[1]);: igual que el anterior pero para el segundo argumento y la matriz @B.
  • for $i (0..$#A): un bucle for. Esto se establece $ien 0 y lo incrementa en uno hasta que tenga el valor del número de elementos en la matriz @A( $#A). Esta es una forma sencilla de iterar sobre todos los elementos de una matriz, ya que $A[$i]será $A[0], $A[1], ... , $A[$#A].
  • $A[$i] eq $B[$i] ? push @S,$A[$i] : last: esta es una notación taquigráfica estilo C. El formato general es foo ? bar : bazy significa "si fooes verdadero, hazlo bar, de lo contrario hazlo" baz. Aquí, estamos probando si el nenésimo (o $ienésimo, en este caso) elemento de la matriz @Aes el mismo que el correspondiente de la matriz @B. Si es así, agréguelo a la tercera matriz, @Ssi no es así, salimos del ciclo con last.
  • print @S: imprime la matriz @S, los elementos compartidos.

Las dos soluciones son muy similares, la única diferencia es que @A=split(/\./,$ARGV[0])se dividirán en ., eliminándolos de la matriz resultante e print join ".", @Simprimirá todos los elementos de @Scon un .entre ellos.

Respuesta3

Como mencioné en los comentarios debajo de la pregunta, encontré una awksolución algo simple: concatenar los dos números para crear una cadena larga, reemplazar todos los puntos con espacios (para permitir el uso del espacio como separador de campo predeterminado en awk) y revise la cadena comparando el campo con el archivo + mitad.

comando básico

printf ${a[1]}${a[2]} | awk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x };}'

Probé esto con gawk y mawk, funcionó en ambos.

Aquí se muestra el resultado con el primer ejemplo ( .1.3.6.1.4.1.232.13600256 y .1.3.6.1.4.1.232.13600276 ):

$ printf ${a[1]}${a[2]} | awk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x };}'
.1.3.6.1.4.1.232

Múltiples comparaciones

Si desea comparar varias cadenas al mismo tiempo, concatenelas y sepárelas con una nueva línea en printf, luego agregue printf al final del comando awk de esta manera:

printf "${a[1]}${a[2]}\n${a[3]}${a[4]}" | awk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x }; printf "\n"}'

Producción:

$ printf "${a[1]}${a[2]}\n${a[3]}${a[4]}" | awk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x }; printf "\n"}'
.1.3.6.1.4.1.232 # same for a[1] and a[2]
.1.3.6.1.4.1.759 # same for a[3] and a[4]

Limitando la salida

Ahora, el comentario de Kos notó apropiadamente que OP quiere que solo se muestren 7 números. Para ello, puede agregar una tubería al cut -d'.' -f1-8comando. Al igual que:

printf "${a[5]}${a[6]}" | mawk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x }; printf "\n"}' | cut -d'.' -f1-8

Aquí hay un ejemplo de salida de mi terminal:

$ a[5]=.1.3.6.1.4.1.232.13600256.885


$ a[6]=.1.3.6.1.4.1.232.13600256.885


$ printf "${a[5]}${a[6]}" | mawk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x }; printf "\n"}' | cut -d'.' -f1-8
.1.3.6.1.4.1.232.13600256.885


 half) ) printf "."$x }; printf "\n"}' | cut -d'.' -f1-8                      <
.1.3.6.1.4.1.232

Simplificando aún más

Nuevamente, todo se puede poner en un script awk.

#!/usr/bin/awk -f

{
 gsub("\\."," "); 
 half=NF/2
}; 

{ 
 for ( x=1; x<=half; x++ ) { 
    if ( $x==$(x + half) ) printf "."$x 
  }; 
  printf "\n"
}

Ejecución de muestra:

$ printf "${a[5]}${a[6]}" | num-comp.awk | cut -d'.' -f1-8                     
.1.3.6.1.4.1.232

Comparación hasta el primer número no igual

Awk tiene una función muy útil substr(string,X,Y)que permite cortar o "recortar" una cadena, desde el primer carácter (x) hasta el final (Y). Entonces, sabiendo eso, tomemos los dos números como dos campos de una cadena y ejecutémoslos a través del ciclo while. Seguiremos aumentando la longitud de la subcadena (de principio a fin) hasta que ya no sean iguales. Una vez que encontramos las subcadenas desiguales, salimos e imprimimos la última subcadena igual conocida.

echo ".1.3.6.1.4.1.232.13600256\t.1.3.6.1.4.1.232.13600276" | awk 'BEGIN{i=1}{ while(substr($1,1,i)==substr($2,1,i)){var=substr($1,1,i);i++};} END{print var}'

Un agradecimiento especial a terdon por sugerir el uso de la función substr, que anteriormente ni siquiera sabía que existía.

Respuesta4

Puede definir una pequeña pythonfunción que puede hacer el trabajo:

#!/usr/bin/env python2
import itertools
def common_portion(a):
    first = a[0].split('.')
    second = a[1].split('.')
    result = []
    for (i, j) in itertools.izip(first, second):
        if i == j:
            result.append(i)
        else:
            break
    return 'Similar part is ' + '.'.join(result)
  • Necesitamos proporcionar una lista que contenga las cadenas que queremos verificar como entrada a la función.

  • firstLa variable contendrá las partes del primer elemento de la lista de entrada dividida en .( a[0].split). De manera similar secondcontendrá las partes del segundo elemento de la lista a.

  • Luego hemos iterado firsty secondverificamos la igualdad de cada elemento con su misma contraparte indexada; si son iguales, uno de ellos se guarda en una lista separada result. Siempre que nos hemos encontrado con la primera diferencia, nos hemos salido del círculo.

  • Finalmente hemos impreso el resultado deseado uniendo los campos con .s ( '.'.join(result))

Prueba :

print common_portion(['.1.3.6.1.4.1.232.13600256', '.1.3.6.1.4.1.232.13600276'])

Similar part is .1.3.6.1.4.1.232


print common_portion(['.1.3.6.1.4.1.759.2344.454545', '.1.3.6.1.4.1.759.3234.454545'])

Similar part is .1.3.6.1.4.1.759

información relacionada