开发者

How do I find common characters between two strings in bash?

开发者 https://www.devze.com 2023-03-25 01:26 出处:网络
For example: s1=\"my_foo\" s2=\"not_my_bar\" the desired result would be my_o. Ho开发者_运维技巧w do I do this in bash? My solution below uses fold to break the string into one character per line,

For example:

s1="my_foo"
s2="not_my_bar"

the desired result would be my_o. Ho开发者_运维技巧w do I do this in bash?


My solution below uses fold to break the string into one character per line, sort to sort the lists, comm to compare the two strings and finally tr to delete the new line characters

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

Alternatively, here is a pure Bash solution (which also maintains the order of the characters). It iterates over the first string and checks if each character is present in the second string.

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

prints: temp_bar


Assuming the strings do not contain embedded newlines:

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" 

And another one:

tr -dc "$s2" <<< "$s1"


a late entry, I've just found this page:

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"

and another one, this one builds a regexp for matching (note: doesn't work with special characters, but that's not that hard to fix with anonther sed)

echo "$str1" |
  grep -E -o ^`echo -n "$str2" | sed 's/\(.\)/(|\1/g'; echo "$str2" | sed 's/./)/g'`


Should be a portable solution:

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


A solution using a single sed execution:

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.*//'

As all cryptic sed script, it needs explanation in the form of a sed script file that can be run by 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.*//

If you want to remove duplicate, add the following at the end of the script:

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

Edit: I realize that dogbane's pure shell solution has the same algorithm, and is probably more efficient.


comm=""
for ((i=0;i<${#s1};i++))
do 
  if test ${s1:$i:1} = ${s2:$i:1}
  then 
    comm=${comm}${s1:$i:1}
  fi
done


Since everyone loves perl one-liners full of punctuation:

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

Creates hashes %a and %b from the input strings.
Prints any characters common to both strings.

outputs:

_moy


"flower","flow","flight" --> output fl

    s="flower"
t="flow"
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
p=$result
q="flight"
j=0

while [ $j -ne ${#p} ]
do
    c1=${p:$j:1}
    if [[ $result1 != *$c1* && $q == *$c1* ]]
    then
      result1=$result1$c1
    fi
    ((j++))
done
echo $result1
0

精彩评论

暂无评论...
验证码 换一张
取 消