Regex Bash计算字符串中的字母,输出总是有点不同

Regex Bash计算字符串中的字母,输出总是有点不同,regex,bash,shell,grep,wc,Regex,Bash,Shell,Grep,Wc,我的剧本有点问题。 我的程序从用户那里接收一个字符串,并将其加在一起,形成循环中的一个大字符串,只有用户在代码中的某个地方键入星号(*)时,循环才会结束。随后,该代码分别计算字母、数字和非字母数字字符。它使用了grep[0-9]| wc的组合。然而输出总是有点疯狂,我给出了几个字符串示例 *=0数字7字母0特殊 a1=2个数字2个字母=0个特殊字母 abc123*=4个数字4个字母0个特殊字符 abc123…*=4个数字4个字母4个特殊字符 ....******=0数字=字母6特殊 换句话说

我的剧本有点问题。 我的程序从用户那里接收一个字符串,并将其加在一起,形成循环中的一个大字符串,只有用户在代码中的某个地方键入星号(
*
)时,循环才会结束。随后,该代码分别计算字母、数字和非字母数字字符。它使用了
grep[0-9]| wc
的组合。然而输出总是有点疯狂,我给出了几个字符串示例

  • *
    =0数字7字母0特殊

  • a1
    =2个数字2个字母=0个特殊字母

  • abc123*
    =4个数字4个字母0个特殊字符

  • abc123…*
    =4个数字4个字母4个特殊字符

  • ....******
    =0数字=字母6特殊

换句话说,它试图添加一个(我想这可能与使用星号有关,但我无法处理),但当我只键入星号时,它会出现一些疯狂的东西

echo $completestring | grep -o "[0-9]*" | wc -c
echo $completestring | grep -o "[a-zA-Z]*" | wc -c
echo $completestring | grep -o "[,._+:@%/-]*" | wc -c
$completestring contains a string written by the user
星号 星号(
*
)与前面的字符或组匹配零次或多次。因此

  • [0-9]*
    匹配任何内容,即数字零次或多次
  • [a-zA-Z]*
    匹配任何字符,即零次或多次匹配范围内的字符
如果要匹配前缀加上零个或多个字符,请使用
*
表达式,例如:

  • [0-9].*
  • [a-zA-Z].*
点(
)与单个字符匹配


一些测试:

$ echo 'test' | grep '[0-9].*'; echo $?
1
$ echo 'test' | grep '[0-9]*'; echo $?
test
0
如果选择了行,则退出状态(
$?
)为0,如果未选择行,则为1

引用 另外请注意,如果要防止重新解释特殊字符:
“$myvar”
,则应将shell变量括在双引号中

计算模式匹配的数量 Grep的
-o
选项只打印匹配行中匹配的非空部分,每个部分都在单独的行中。因此,匹配部分的计数等于输出中的行数。因此您需要
wc-l

$ echo 'abc123' | grep -o '[a-z]' | wc -l 
3

$ echo 'abc123def' | grep -o '[a-z]\+' 
abc
def

如果要计算特定类型字符的实例数,可以执行以下操作:

echo $completestring | grep -o "[0-9]" | wc -l
echo $completestring | grep -o "[a-zA-Z]" | wc -l
echo $completestring | grep -o "[,._+:@%/-]" | wc -l
例如,这将为给定的完整字符串提供以下输出:

completestring=”foo@a321abcdr%20:/mango/25b“

grep匹配:
3
2
1
2
0
2
5

echo $completestring | grep -o "[a-zA-Z]" | wc -l
15
grep匹配:
f
o
o
a
a
b
c
d
r
m
a
n
g
o
b

echo $completestring | grep -o "[,._+:@%/-]" | wc -l
5
grep匹配:
@
%
/
/

如果要将数字和单词的簇作为单个实例进行计数(例如,mango应为1而不是5,321应为1而不是3),则可以使用以下方法:

echo $completestring | grep -o "[0-9][0-9]*" | wc -l
echo $completestring | grep -o "[a-zA-Z][a-zA-Z]*" | wc -l

我认为特殊字符数是以每个字符为基础的。

你的想法有几个问题

首先,请,请,无论如何:引用您的变量展开式

  • 引述 以下是在某个目录中发生的情况:

    $ completestring=.*    ;   echo $completestring
    . .. .directory .#screenon
    
    相反,我相信你想要:

    $completestring=.*;回显“$completestring” *

  • 使用wc将计数字节,而不是字符(接近UNICODE代码点)。示例(在utf-8的控制台中,现在几乎都是这样):

  • 此外,wc正在计算尾随的新行

    $ echo "123" | wc -c
    4
    
    您需要使用
    echo-n
    (不可移植,不推荐)或
    printf“%s”

    $ printf '%s' "123" | wc -c
    3
    
  • 使用带grep的星号可以打印每行中的字符:

    $ completestring="jkfdsnlal92845t02u74ijopzidjb jd"
    
    $ echo $completestring | grep -o [0-9]*
    92845
    02
    74
    
    没有简单的计算方法。一种简化方法是仅使用范围:

    $ echo $completestring | grep -o [0-9]
    9
    2
    8
    4
    5
    0
    2
    7
    4
    
    然后你可以数行:

    $ echo $completestring | grep -o [0-9] | wc -l
    9
    
    注意:从这里开始,我将只使用一个as变量。
    更容易输入,希望您理解:)

  • 如果在输入结束时使用星号,则应避免在测试字符串中包含星号
    *
    。根据您读取变量的方式,您可以使用Ctrl-D向系统发送一个
    EOF
    信号,以结束从用户处读取输入

  • 使用完整bash: 但我们可以通过简单的bash构造完成所有需要的工作:

    $ a="jkfdsnlal92845t02u74ijopzidjb jd"
    $ b="${#a//[^0-9]}"                       # remove all characters 
                                              # that are not decimal digits
    
    $ echo "${b}"                             # Not really needed, but this  
    928450274                                 # what var b contains.
    
    $ echo "${#b}"                            # Print the length of var b.
    9
    
    您在代码中编写的内容可以翻译成这样(需要将
    /
    引用为
    \/
    ,我将
    *
    包含在特殊列表中)

    将打印

    Digits=3  Alpha=3  Special=1
    
    立法会 但是,此系统存在问题。
    它还将计算许多UNICODE字符:

    $ c=aßbéc123*; a=${c//[^a-zA-Z]}; echo "string=$a    count=${#a}"
    string=aßbéc    count=5
    
    我相信这就是你需要的

    但如果必须限制为128个ascii字符,则在执行范围选择时,将LC_ALL或更具体地说,LC_COLLATE更改为C语言环境:

    $ (LCcompletestring=abc123*; alpha=${completestring//[^a-zA-Z]}; alpha=${#alpha}; echo "${alpha}"_COLLATE=C a=${c//[^a-zA-Z]}; echo "string=$a    count=${#a}")
    string=abc    count=3
    
    (…)是使用子shell,避免在整个shell中设置LC\U COLLATE。
    但是,您可以在脚本开始时设置它,它也可以工作

    对不起,时间太长了。但不管怎样,我还是错过了什么吗


    嗯,是的,我希望您的密码不会包含控制字符(C0:ASCII从1到31和127,以及C1:128到159)。因为数一数有几处曲折。可能超出了这个答案。

    你想让它说a1=1个字母1个数字和0个特殊数字吗?额外的计数与正在计数的新行有关,因为你在
    -c
    中使用
    -wc
    ,如果你匹配
    [0-9]*
    匹配
    我想你的回答不会回答这个问题question@AhmedMasud为什么?我可能弄错了,但是你的解决方案会显示grep是否在字符串中找到某个内容。我尝试数一数,例如:如果我有字符串“aabbcc123123…”,那么我希望我的代码是:6个字母,6个数字,5个特殊字符(包括aster)
    completestring=abc123*
    dig=${completestring//[^0-9]}; dig=${#dig}
    alpha=${completestring//[^a-zA-Z]}; alpha=${#alpha}
    special=${completestring//[^,._+:@%\/*-]}; special=${#special}
    echo "Digits=$dig  Alpha=$alpha  Special=$special"
    
    Digits=3  Alpha=3  Special=1
    
    $ c=aßbéc123*; a=${c//[^a-zA-Z]}; echo "string=$a    count=${#a}"
    string=aßbéc    count=5
    
    $ (LCcompletestring=abc123*; alpha=${completestring//[^a-zA-Z]}; alpha=${#alpha}; echo "${alpha}"_COLLATE=C a=${c//[^a-zA-Z]}; echo "string=$a    count=${#a}")
    string=abc    count=3