使用Bash计算字符串中字符出现的次数

172

我需要使用Bash计算字符串中某个字符的出现次数。

在下面的示例中,当字符为(例如)t时,它会正确地输出vart的出现次数,但是当字符为逗号或分号时,它会输出零:

var = "text,text,text,text" 
num = `expr match $var [,]`
echo "$num"

http://unix.stackexchange.com/questions/18736/how-to-count-the-number-of-a-specific-character-in-each-line - Ciro Santilli OurBigBook.com
10个回答

175
您可以例如去除所有其他字符并计算剩余的内容,如下所示:
var="text,text,text,text"
res="${var//[^,]}"
echo "$res"
echo "${#res}"

将会打印

,,,
3

或者

tr -dc ',' <<<"$var" | awk '{ print length; }'

或者

tr -dc ',' <<<"$var" | wc -c    #works, but i don't like wc.. ;)

或者

awk -F, '{print NF-1}' <<<"$var"

或者

grep -o ',' <<<"$var" | grep -c .

或者

perl -nle 'print s/,//g' <<<"$var"

1
类似于 y="${x//[^s|S]}"; echo "${#y}"更多技巧 - Aquarius Power
7
在使用大量迭代循环时,应该避免使用生成另一个进程来执行此类工作,因为这样会严重影响性能。通常情况下,在使用迭代或重复操作时,外部进程执行应该是最后的选择。请使用第一个方法。 - osirisgothra
在我看来,代码块4是最好的。我们需要让它更易于使用: tr -dc ',' <<<"$var" | wc -c - bgStack15
2
@bgStack15的代码块1没有初始化额外的进程,如果你有大量需要解析的行,它可能会有更好的性能。 - petertc
1
@Robert,因为wc在输入时错误地计算了行数。当然,wc -c是可以的,但由于行计数问题,我不喜欢它。例如,printf "line1\nline2\n" | wc -l打印出2,但printf "line1\nline2" | wc -l只打印出1 - clt60
显示剩余10条评论

129

我会使用以下的 awk 命令:

string="text,text,text,text"
char=","
awk -F"${char}" '{print NF-1}' <<< "${string}"

我将字符串按照$char分割,并打印结果字段数减去1。

如果你的 shell 不支持 <<< 操作符,请使用 echo

echo "${string}" | awk -F"${char}" '{print NF-1}'

5
请使用以下命令:$(grep -o "$needle" < filename | wc -l)。该命令可以在文件中查找指定的字符串并统计出现次数。 - hek2mgl
13
@Amir 你期望什么? - hek2mgl
3
你可以跳过wc -l,直接使用grep -c,它适用于BSD grep和Linux grep。 - andsens
8
grep -c 只会输出匹配行的数量,不计算每行中多个匹配项的次数。 - hek2mgl
1
我想在一个字符串中计算 '$' 的数量,如何从主字符串中转义 '$'? - masT
显示剩余4条评论

112
你可以通过组合 trwc 命令来完成。例如,要在字符串 referee 中计算字母 e 的数量。
echo "referee" | tr -cd 'e' | wc -c

输出

4

说明:命令tr -cd 'e'会去除除了'e'以外的所有字符,而命令wc -c会计算剩余字符的数量。

对于这个解决方案来说,多行输入也是可以的,比如命令cat mytext.txt | tr -cd 'e' | wc -c可以统计文件mytext.txt中的e数量,即使文件中可能包含很多行。

*** 更新 ***

为了解决数字前面的多个空格(@tom10271),只需添加一个管道后跟tr命令:

 tr -d ' '
例如:
echo "referee" | tr -cd 'e' | wc -c | tr -d ' '

1
在macOS上,输出内容前面包含多个空格 - tom10271
非常感谢tr,它似乎是一个我不知道的很棒的工具。今天我学到了新东西! - j.c

13
awk非常酷,但为什么不保持简单呢?
num=$(echo $var | grep -o "," | wc -l)

你可以将其作为一个函数进行重复使用。
# usage: echo "1,2,3,4,5" | text.count # outputs 4
function text.count(){
    grep -o "$1" | wc -l
}

7

在大家的出色答案和评论的基础上,这是最简短、最简单明了的版本:

grep -o "$needle" <<< "$haystack" | wc -l

该命令使用grep命令从给定的文本中查找指定字符串,并使用wc -l计算其出现次数。

2

如果您的服务器已经安装了awk,那么它将可以很好地工作。

var="text,text,text,text" 
num=$(echo "${var}" | awk -F, '{print NF-1}')
echo "${num}"

只是提醒一下:awk -F,查找逗号。你可以这样做:awk -F"${your_char}" - Emixam23

2
此外,例如我们想要计数t
echo "test" | awk -v RS='t' 'END{print NR-1}'

或者在 Python 中。
python -c 'print "this is for test".count("t")'

甚至更好的是,我们可以使用 awk 使我们的脚本动态化。

echo 'test' | awk '{for (i=1 ; i<=NF ; i++) array[$i]++ } END{ for (char in array) print char,array[char]}' FS=""

在这种情况下,输出如下:
e 1
s 1
t 2

1
我建议如下:

var="any given string"
N=${#var}
G=${var//g/}
G=${#G}
(( G = N - G ))
echo "$G"

没有调用任何其他程序。

0
这里提供的所有 awk 解决方案都会在文本中出现换行符时中断。例如:
text="one,two,thr
ee,four"
DELIM=','
count=$( awk -F"$DELIM" '{print NF-1}' <<<"${text}" )
echo $count

结果:

2
1

同时能正确处理换行符的解决方案是:

text="one,two,thr
ee,four"
DELIM=','
count=$( awk 'BEGIN{RS="'"$DELIM"'";FS=""}END{print NR-1}' <<<"${text}" )
echo $count

结果是3


0

从文件中计算固定字符串(-F)的数量

export searchpattern=$(echo ",")

echo "text,text,text,text" | tr "," '\n' | sed 's/$/,/g' > filename

export count=$(grep -F $searchpattern filename | wc -l)

echo "$count-1" | bc


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接