Как мне найти общие символы между двумя строками в bash? - PullRequest
5 голосов
/ 04 августа 2011

Например:

s1="my_foo"
s2="not_my_bar"

желаемый результат будет my_o.Как мне сделать это в Bash?

Ответы [ 7 ]

9 голосов
/ 04 августа 2011

Мое решение ниже использует fold для разбиения строки на один символ в строке, sort для сортировки списков, comm для сравнения двух строк и, наконец, tr для удаления символов новой строки

comm -12 <(fold -w1 <<< $s1 | sort -u) <(fold -w1 <<< $s2 | sort -u) | tr -d '\n'

В качестве альтернативы здесь приведено чистое решение Bash (которое также поддерживает порядок символов). Он перебирает первую строку и проверяет, присутствует ли каждый символ во второй строке.

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

отпечатков: temp_bar

2 голосов
/ 07 августа 2011

поздняя запись, я только что нашел эту страницу:

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"

и еще одну, эта создает регулярное выражение для соответствия (примечание: не работает со специальными символами, но это не тактрудно исправить с помощью другого седа)

echo "$str1" |
  grep -E -o ^`echo -n "$str2" | sed 's/\(.\)/(|\1/g'; echo "$str2" | sed 's/./)/g'`
2 голосов
/ 04 августа 2011

Предполагая, что строки не содержат встроенных символов новой строки:

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" 

И еще один:

tr -dc "$s2" <<< "$s1"
1 голос
/ 04 августа 2011

Решение, использующее одно выполнение 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.*//'

Как и все загадочные сценарии sed, требуется пояснение в виде файла сценария sed, который может запускаться 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.*//

Если вы хотите удалить дубликаты, добавьте следующее в конец скрипта:

:end;s/\(.\)\(.*\)\1/\1\2/;t end

Редактировать: Я понимаю, что чистое решение оболочки на кнуте имеет тот же алгоритм и, вероятно, более эффективно.

1 голос
/ 04 августа 2011
comm=""
for ((i=0;i<${#s1};i++))
do 
  if test ${s1:$i:1} = ${s2:$i:1}
  then 
    comm=${comm}${s1:$i:1}
  fi
done
1 голос
/ 04 августа 2011

Должно быть портативное решение:

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
0 голосов
/ 06 ноября 2015

Так как все любят однострочники perl, заполненные пунктуацией:

perl -e '$a{$_}++ for split "",shift; $b{$_}++ for split "",shift; for (sort keys %a){print if defined $b{$_}}' my_foo not_my_bar

Создает хэши %a и %b из входных строк.
Печатает любые символы, общиена обе строки.

вывод:

_moy
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...