计算字符串中某个子字符串出现的次数

计算字符串中某个子字符串出现的次数

如何使用 Bash 计算字符串中子字符串出现的次数?

例子:

我想知道这个子字符串出现了多少次:

Bluetooth
         Soft blocked: no
         Hard blocked: no

...出现在这个字符串中...

0: asus-wlan: Wireless LAN
         Soft blocked: no
         Hard blocked: no
1: asus-bluetooth: Bluetooth
         Soft blocked: no
         Hard blocked: no
2: phy0: Wireless LAN
         Soft blocked: no
         Hard blocked: no
113: hci0: Bluetooth
         Soft blocked: no
         Hard blocked: no

注一:我尝试过使用 sed、grep、awk 等多种方法...当我们有带有空格和多行的字符串时,似乎没有任何效果。

注二:我是一名 Linux 用户,我正在尝试一种解决方案,该解决方案不涉及安装 Linux 发行版中常见的应用程序/工具之外的应用程序/工具。


重要的:

我想要像下面假设的例子这样的东西。在本例中我们使用两个Shell 变量 (Bash)

例子:

STRING="0: asus-wlan: Wireless LAN
         Soft blocked: no
         Hard blocked: no
1: asus-bluetooth: Bluetooth
         Soft blocked: no
         Hard blocked: no
2: phy0: Wireless LAN
         Soft blocked: no
         Hard blocked: no
113: hci0: Bluetooth
         Soft blocked: no
         Hard blocked: no"

SUB_STRING="Bluetooth
         Soft blocked: no
         Hard blocked: no"

awk -v RS='\0' 'NR==FNR{str=$0; next} {print gsub(str,"")}' "$STRING" "$SUB_STRING"

笔记:我们使用 awk 只是为了说明!

答案1

perl

printf '%s' "$SUB_STRING" |
  perl -l -0777 -ne '
    BEGIN{$sub = <STDIN>}
    @matches = m/\Q$sub\E/g;
    print scalar @matches' <(printf '%s' "$STRING")

独自bash一人,你总是可以做这样的事情:

s=${STRING//"$SUB_STRING"}
echo "$(((${#STRING} - ${#s}) / ${#SUB_STRING}))"

$s包含,其中删除了$STRING所有出现的内容。我们通过计算和之间的字符数差除以自身的长度来$SUB_STRING找出被删除的 s 的数量。$SUB_STRING$STRING$s$SUB_STRING

POSIXly,你可以这样做:

s=$STRING count=0
until
  t=${s#*"$SUB_STRING"}
  [ "$t" = "$s" ]
do
  count=$((count + 1))
  s=$t
done
echo "$count"

答案2

如果子字符串不包含换行符:

echo -n STRING | grep -Fo SUBSTRING | wc -l

答案3

使用字符串处理函数,我们可以用 Perl 来完成,如下所示:

 printf '%s\n' "$STRING" |
 perl -nse '
      $_ .= join "", <>;
      $k++ while ++($p = index($_, $s, $p));
      print $k, "\n" ;
 '    --     -s="$SUB_STRING" 

解释:

° load up the whole string in $_

°  index function will return the position of a substring in a string OTW returns -1

° progressively match the substring and use the position found as the starting position for the next search. 

°  all this while increment the counter $k depicting substring found. 

下面列出了一些其他方法:

读取字符串并使用正则表达式。

printf '%s\n' "$STRING" |
perl -slp -0777e '
        $_ = () = /$s/g;
 ' -- -s="$s" 

° 将字符串放入 $_ 变量中。

° 使用 -s 选项将子字符串从命令行传递到 perl。

° 现在对 $_ 执行匹配,并在列表上下文中获取匹配项,然后在标量上下文中获取匹配项以获取匹配项的数量。

° -p 选项将自动打印 $_ 中的内容。

使用sed工具的方法:

 esc_s=$(printf '%s\n' "$SUB_STRING" |\
 sed -e 's:[][\/.^$*]:\\&:g' -e 'H;1h;$!d;g;s/\n/\\n/g')

 printf '%s\n' "$STRING" |
 sed -ne '
         $!{N;s/^/\n/;D;}
         /'"$esc_s"'/{
               x;p;x
               s///;s/^/\n/;D
         }
 ' | wc -l

° 作为准备步骤,我们继续将所有充当元字符的字符转义到子字符串中 s/// 语句的左侧,如果不这样做,将导致 sed 崩溃。

° 现在我们将整个字符串放入模式空间。

° 然后我们继续打印一个空行,保留空间是一个很好的候选者,并从模式空间中取出子字符串。

° 冲洗...起泡沫...只要存在子字符串就重复。

° 然后将空行通过管道传输到 wc 工具,该工具将为我们提供行数 = 找到子字符串的次数。

这是外壳版本:

 e=$STRING  N=0
 while 
     e=$(expr " $e" : " \(.*\)$SUB_STRING")
     case $e in "" ) break ;; esac
  do
           N=$(expr "$N" + 1)
  done
  echo "$N"

答案4

gawk '
END { print NR - 1 }
' RS='Bluetooth
         Soft blocked: no
         Hard blocked: no' input.txt

解释

RS- 输入记录分隔符,默认为换行符。将其设置为所需的字符串,并将awk使用该字符串作为分隔符将所有文本拆分为记录。然后,只打印1END部分中减去的记录数。

使用变量:

#!/bin/bash

STRING='0: asus-wlan: Wireless LAN
         Soft blocked: no
         Hard blocked: no
1: asus-bluetooth: Bluetooth
         Soft blocked: no
         Hard blocked: no
2: phy0: Wireless LAN
         Soft blocked: no
         Hard blocked: no
113: hci0: Bluetooth
         Soft blocked: no
         Hard blocked: no'

SUB_STRING='Bluetooth
         Soft blocked: no
         Hard blocked: no'

gawk 'END { print NR - 1 }' RS="$SUB_STRING" <<< "$STRING"

相关内容