bash中if语句中的模式匹配

时间:2017-08-22 01:27:19

标签: regex bash

我试图计算目录中所有.txt文件中至少有两个元音的单词。到目前为止,这是我的代码:

#!/bin/bash

wordcount=0


for i in $HOME/*.txt
do
cat $i |
while read line
do
    for w in $line
    do
    if [[ $w == .*[aeiouAEIOU].*[AEIOUaeiou].* ]]
    then
        wordcount=`expr $wordcount + 1`
        echo $w ':' $wordcount  
    else
        echo "In else"
    fi
    done
done
echo $i ':' $wordcount
wordcount=0
done

以下是来自txt文件的示例

  

最后修改日期:Sun Aug 20 18:18:27 IST 2017
  删除PPA
  sudo apt-get install ppa-purge
  sudo ppa-purge ppa:

问题是它与if语句中的模式不匹配文本文件中的所有单词。它直接转到else语句。其次,echo $ i'中的wordcount:' $ wordcount等于0,应该是某个值。

3 个答案:

答案 0 :(得分:3)

即时问题:Glob vs Regex

[[ $string = $pattern ]]没有执行正则表达式匹配;相反,它是一个全球风格的模式匹配。虽然.表示"任何字符"在正则表达式中,它只在glob中匹配。

你有几个选择:

  1. 使用=~代替执行正则表达式匹配:

    [[ $w =~ .*[aeiouAEIOU].*[AEIOUaeiou].* ]]
    
  2. 使用glob样式表达式而不是正则表达式:

    [[ $w = *[aeiouAEIOU]*[aeiouAEIOU]* ]]
    

    请注意在此处使用=而不是==;虽然两者在技术上都是有效的,但前者避免构建手指内存,这会在为test / [的POSIX实现编写代码时导致错误,因为= is the only valid string comparison operator

  3. 更大的问题:正确地逐字阅读

    使用for w in $line天生就不安全了。使用read -a将一行读入一个单词数组:

    #!/usr/bin/env bash
    
    wordcount=0
    for i in "$HOME"/*.txt; do
      while read -r -a words; do
        for word in "${words[@]}"; do
          if [[ $word = *[aeiouAEIOU]*[aeiouAEIOU]* ]]; then
            (( ++wordcount ))
          fi
        done
      done <"$i"
      printf '%s: %s\n' "$i" "$wordcount"
      wordcount=0
    done
    

答案 1 :(得分:1)

尝试:

awk '/[aeiouAEIOU].*[AEIOUaeiou]/{n++} ENDFILE{print FILENAME":"n; n=0}' RS='[[:space:]]' *.txt

示例输出如下:

$ awk '/[aeiouAEIOU].*[AEIOUaeiou]/{n++} ENDFILE{print FILENAME":"n; n=0}' RS='[[:space:]]' *.txt
one.txt:1
sample.txt:9

工作原理:

  • /[aeiouAEIOU].*[AEIOUaeiou]/{n++}

    每当我们找到带有两个元音的单词时,我们会增加变量n

  • ENDFILE{print FILENAME":"n; n=0}

    在每个文件的末尾,我们打印文件的名称和2元音字数n。然后,我们将n重置为零。

  • RS='[[:space:]]'

    这告诉awk使用任何空格作为单词分隔符。这使每个单词成为记录。 Awk一次读取输入的一条记录。

Shell问题

awk的使用避免了大量的shell问题。例如,考虑行for w in $line。这不会按你希望的方式运作。考虑一个包含这些文件的目录:

$ ls
one.txt  sample.txt

现在,让我们line='* Item One'看看会发生什么:

$ line='* Item One'
$ for w in $line; do echo "w=$w"; done
w=one.txt
w=sample.txt
w=Item
w=One

shell将*中的line视为通配符,并将其展开为文件列表。赔率是你不想要这个。 awk解决方案避免了这样的各种问题。

答案 2 :(得分:0)

使用grep - 这很简单。

#!/bin/bash

wordcount=0
for file in ./*.txt
do
count=`cat $file | xargs -n1 | grep -ie "[aeiou].*[aeiou]" | wc -l`
wordcount=`expr $wordcount + $count`
done

echo $wordcount