如何使用 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
使用该字符串作为分隔符将所有文本拆分为记录。然后,只打印1
该END
部分中减去的记录数。
使用变量:
#!/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"