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
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
sed
a trabajar en dos líneas. Esto se hace usandoN
y 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
%s
en laprintf
cadena de formato y antes\1
en 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 deprint
y 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$i
en 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 esfoo ? bar : baz
y significa "sifoo
es verdadero, hazlobar
, de lo contrario hazlo"baz
. Aquí, estamos probando si eln
enésimo (o$i
enésimo, en este caso) elemento de la matriz@A
es el mismo que el correspondiente de la matriz@B
. Si es así, agréguelo a la tercera matriz,@S
si no es así, salimos del ciclo conlast
.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 ".", @S
imprimirá todos los elementos de @S
con un .
entre ellos.
Respuesta3
Como mencioné en los comentarios debajo de la pregunta, encontré una awk
solució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-8
comando. 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 python
funció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.
first
La variable contendrá las partes del primer elemento de la lista de entrada dividida en.
(a[0].split
). De manera similarsecond
contendrá las partes del segundo elemento de la listaa
.Luego hemos iterado
first
ysecond
verificamos la igualdad de cada elemento con su misma contraparte indexada; si son iguales, uno de ellos se guarda en una lista separadaresult
. 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