【问题标题】:How do I find common characters between two strings in bash?如何在 bash 中找到两个字符串之间的共同字符?
【发布时间】:2011-08-04 13:04:46
【问题描述】:

例如:

s1="my_foo"
s2="not_my_bar"

想要的结果是my_o。我如何在 bash 中做到这一点?

【问题讨论】:

  • 下划线将是分隔符?
  • 不,问题是我想从 s1 和 s2 中获取所有常见字符
  • shell 脚本中任务的简单性和解决方案的复杂性之间的极端差异。非常好!

标签: string bash shell


【解决方案1】:

我下面的解决方案使用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

【讨论】:

  • 您的第二种方法的缺点是不能使用ts 中的空格。至少以目前的形式。也比较长。
【解决方案2】:

假设字符串不包含嵌入的换行符:

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"

【讨论】:

  • 您使用tr 的第二个解决方案很好,但不会删除重复项。
  • @dogbane,好点子!我应该提到这一点。要删除重复项,两个值都应通过 fold .. | sort .. 过滤器。
【解决方案3】:

一个迟到的条目,我刚刚找到这个页面:

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"

还有一个,这个构建了一个用于匹配的正则表达式(注意:不适用于特殊字符,但用另一个 sed 修复并不难)

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

【讨论】:

  • 使用awk 是个好主意,但使用此示例awk 'BEGIN{FS=""} { n=0; while(n&lt;=NF) {if ($n == substr(test,n,1)) {printf("%c",$n);} n++;} print ""}' test="/aa/ba/" &lt;&lt;&lt; "/aa/bb/" 不起作用。它显示/aa/b/ 而不是/aa/b。请尝试修正您的答案。干杯
  • @olibre: 奇怪的报告 :) 修复了它。
【解决方案4】:

应该是一个便携的解决方案:

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

【讨论】:

  • 这仅匹配两个字符串中相同索引处的字符。所以如果你有my_foo_barmy_bar 就行不通了。
【解决方案5】:

使用单个 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脚本,需要以echo -e "$s1\n$s2" | sed -f script可以运行的sed脚本文件的形式进行解释:

# 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

编辑:我意识到 dogbane 的纯 shell 解决方案具有相同的算法,并且可能更有效。

【讨论】:

    【解决方案6】:
    comm=""
    for ((i=0;i<${#s1};i++))
    do 
      if test ${s1:$i:1} = ${s2:$i:1}
      then 
        comm=${comm}${s1:$i:1}
      fi
    done
    

    【讨论】:

      【解决方案7】:

      由于每个人都喜欢充满标点符号的 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
      

      【讨论】:

        【解决方案8】:
        "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
        

        【讨论】:

          猜你喜欢
          • 2010-09-25
          • 2013-09-13
          • 2022-11-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多