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

时间:2016-12-07 05:55:43

标签: 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个特殊

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

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

3 个答案:

答案 0 :(得分:2)

星号

星号(*)匹配前一个字符或一组或更多次。因此

  • [0-9]*匹配任何内容,即数字或更多次;
  • [a-zA-Z]*匹配任何内容,即 0 范围内的字符或更多次。

如果要匹配前缀加上零个或多个字符,请使用.*表达式,例如:

  • [0-9].*;
  • [a-zA-Z].*

点(.)匹配单个字符。

一些测试:

$ echo 'test' | grep '[0-9].*'; echo $?
1
$ echo 'test' | grep '[0-9]*'; echo $?
test
0

退出状态($?)为0,如果选择了一行,如果没有选择行,则为1。

引用

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

计算模式匹配的数量

Grep的-o选项仅打印匹配行的匹配非空部分,每个此类部分位于单独的行上。因此,匹配部分的数量等于输出中的行数。所以你需要wc -l代替:

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

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

答案 1 :(得分:2)

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

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:/芒果/ 25B”

echo $completestring | grep -o "[0-9]" | wc -l
7

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 {{1} } r m a n g o

b

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

如果你想将数字和单词的数量作为单个实例计算(例如芒果应该是1而不是5而321应该算作1而不是3),那么你可以使用类似的东西:

/

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

答案 2 :(得分:1)

您的想法有几个问题。

首先,请一定请:引用您的变量扩展。

  1. 引用 这是在某些目录中发生的事情:

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

    $ completedtring =。*; echo“$ completedtring” *

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

    $ echo "école" | wc -c 
    7
    
    $ echo "ß" | wc -c
    3
    
  3. 此外,wc正在计算尾随的新行。

    $ echo "123" | wc -c
    4
    

    您需要使用echo -n(非便携式,不推荐使用)或printf '%s'

    $ printf '%s' "123" | wc -c
    3
    
  4. 使用带有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
    

    注意:我将仅使用此处的变量。
       更容易打字,希望你明白:)。

    echo $completestring | grep -o [0-9]*
    
  5. 如果用于输入结尾,则应避免在测试字符串中包含*星号。根据您读取变量的方式,也许您可​​以使用 Ctrl - D 向系统发出EOF信号,以结束用户的读取输入。 / p>

  6. 使用完整的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
    

    您在代码中所写的内容可以转换为此内容(/需要引用为\/,我将*包含在特殊列表中。

    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
    

    LC_COLLATE

    然而,这个系统存在问题 它也将计算许多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_COLLATE 但是,您可以在脚本的开头设置它,它也可以工作。

    这很长,抱歉。但无论如何:我还缺少什么吗?

    嗯,是的,我希望您的密码不会包含控制字符(C0:ASCII从1到31和127,以及C1:128到159)。因为计算它们有几个曲折。可能在这个答案之外。