Por ejemplo:¿Cómo encuentro caracteres comunes entre dos cadenas en bash?
s1="my_foo"
s2="not_my_bar"
el resultado deseado sería my_o
. ¿Cómo hago esto en bash?
Por ejemplo:¿Cómo encuentro caracteres comunes entre dos cadenas en bash?
s1="my_foo"
s2="not_my_bar"
el resultado deseado sería my_o
. ¿Cómo hago esto en bash?
Mi solución a continuación utiliza fold
para romper la cadena en un solo personaje por línea, sort
para ordenar las listas, comm
comparar las dos cadenas y finalmente tr
eliminar los caracteres de nueva línea
comm -12 <(fold -w1 <<< $s1 | sort -u) <(fold -w1 <<< $s2 | sort -u) | tr -d '\n'
Alternativamente, aquí es una solución pura de Bash (que también mantiene el orden de los caracteres). Se repite sobre la primera cadena y comprueba si cada carácter está presente en la segunda cadena.
s="temp_foo_bar"
t="temp_bar"
i=0
while [ $i -ne ${#s} ]
do
c=${s:$i:1}
if [[ $result != *$c* && $t == *$c* ]]
then
result=$result$c
fi
((i++))
done
echo $result
impresiones: temp_bar
Sí, añadiré -u al comando de ordenación también. –
Buen uso del comodín: +1. – jfg956
Su segundo método tiene el vicio de no trabajar con espacios en 't' y' s'. Al menos en su forma actual. También es bastante largo. –
debe ser una solución portátil:
s1="my_foo"
s2="my_bar"
while [ -n "$s1" -a -n "$s2" ]
do
if [ "${s1:0:1}" = "${s2:0:1}" ]
then
printf %s "${s1:0:1}"
else
break
fi
s1="${s1:1:${#s1}}"
s2="${s2:1:${#s2}}"
done
Esto solo coincide con los caracteres en el mismo índice en ambas cadenas. Entonces no funcionará si tiene 'my_foo_bar' y' my_bar'. – dogbane
Suponiendo que las cadenas no contienen nuevas líneas incrustadas:
s1='my_foo' s2='my_bar'
intersect=$(
comm -12 <(
fold -w1 <<< "$s1" |
sort -u
) <(
fold -w1 <<< "$s2" |
sort -u
) |
tr -d \\n
)
printf '%s\n' "$intersect"
Y otro:
tr -dc "$s2" <<< "$s1"
Su segunda solución con 'tr' es agradable, pero no elimina los duplicados. – dogbane
@dogbane, buen punto! Debería haber mencionado eso. Para eliminar duplicados, ambos valores deben pasar el 'fold .. | sort ..' filter. –
comm=""
for ((i=0;i<${#s1};i++))
do
if test ${s1:$i:1} = ${s2:$i:1}
then
comm=${comm}${s1:$i:1}
fi
done
Una solución utilizando una sola ejecución SED:
echo -e "$s1\n$s2" | sed -e 'N;s/^/\n/;:begin;s/\n\(.\)\(.*\)\n\(.*\)\1\(.*\)/\1\n\2\n\3\4/;t begin;s/\n.\(.*\)\n\(.*\)/\n\1\n\2/;t begin;s/\n\n.*//'
Como toda comando especialmente críptico, sino que precisa de una explicación en forma de un archivo de secuencia de comandos sed que puede ser ejecutado por echo -e "$s1\n$s2" | sed -f script
:
# Read the next line so s1 and s2 are in the pattern space only separated by a \n.
N
# Put a \n at the beginning of the pattern space.
s/^/\n/
# During the script execution, the pattern space will contain <result so far>\n<what left of s1>\n<what left of s2>.
:begin
# If the 1st char of s1 is found in s2, remove it from s1 and s2, append it to the result and do this again until it fails.
s/\n\(.\)\(.*\)\n\(.*\)\1\(.*\)/\1\n\2\n\3\4/
t begin
# When previous substitution fails, remove 1st char of s1 and try again to find 1st char of S1 in s2.
s/\n.\(.*\)\n\(.*\)/\n\1\n\2/
t begin
# When previous substitution fails, s1 is empty so remove the \n and what is left of s2.
s/\n\n.*//
Si desea eliminar el duplicado, agregue lo siguiente al final del script:
Edición: Me doy cuenta de que la solución de caparazón puro de dogbane tiene el mismo algoritmo, y es probablemente más eficiente.
una entrada tardía, me acabo de encontrar esta página:
echo "$str2" |
awk 'BEGIN{FS=""}
{ n=0; while(n<=NF) {
if ($n == substr(test,n,1)) { if(!found[$n]) printf("%c",$n); found[$n]=1;} n++;
} print ""}' test="$str1"
y otro, éste construye una expresión regular de la aceptación (nota: no funciona con caracteres especiales, pero eso no es tan difícil para fijar con anonther sed)
echo "$str1" |
grep -E -o ^`echo -n "$str2" | sed 's/\(.\)/(|\1/g'; echo "$str2" | sed 's/./)/g'`
Buena idea para usar 'awk' pero no funciona usando este ejemplo' awk 'BEGIN {FS = ""} {n = 0; while (n <= NF) {if ($ n == substr (test, n, 1)) {printf ("% c", $ n);} n ++;} print ""} 'test = "/ aa/ba/"<<<"/aa/bb/"'. Muestra '/ aa/b /' en lugar de '/ aa/b'. Intenta corregir tu respuesta. Cheers – olibre
@olibre: informe raro :) lo arregló. –
Puesto que cada uno ama perl algunas frases llenas de puntuacion:
perl -e '$a{$_}++ for split "",shift; $b{$_}++ for split "",shift; for (sort keys %a){print if defined $b{$_}}' my_foo not_my_bar
Crea hashes %a
y %b
desde las cadenas de entrada.
Imprime los caracteres comunes a ambas cadenas.
salidas:
_moy
el guión bajo será el delimitador? – ajreal
no, la cuestión es que quiero obtener todos los caracteres comunes de s1 y s2 – johannes
diferencia extrema entre la simplicidad de la tarea y la complejidad de la solución en la creación de scripts en shell. ¡muy agradable! –