【问题标题】:use bash count every word's occurrence in a file使用 bash 计算文件中每个单词的出现次数
【发布时间】:2012-09-03 03:03:15
【问题描述】:

我想计算文件中每个单词的出现次数 但结果是错误的。

#!/bin/bash
#usage: count.sh file

declare -a dict

for word in $(cat $1)
do
    if [ ${dict[$word]} == "" ] ;then
        dict[$word]=0
    else
        dict[$word]=$[${dict[$word]} + 1]
    fi
done

for word in ${!dict[@]}
do
    echo $word: ${dict[$word]}
done

使用下面的测试文件:

learning the bash shell
this is second line
this is the last line

bash -x count.sh 文件 得到结果:

+ declare -a dict
++ cat book
+ for word in '$(cat $1)'
+ '[' '' == '' ']'
+ dict[$word]=0
+ for word in '$(cat $1)'
+ '[' 0 == '' ']'
+ dict[$word]=1
+ for word in '$(cat $1)'
+ '[' 1 == '' ']'
+ dict[$word]=2
+ for word in '$(cat $1)'
+ '[' 2 == '' ']'
+ dict[$word]=3
+ for word in '$(cat $1)'
+ '[' 3 == '' ']'
+ dict[$word]=4
+ for word in '$(cat $1)'
+ '[' 4 == '' ']'
+ dict[$word]=5
+ for word in '$(cat $1)'
+ '[' 5 == '' ']'
+ dict[$word]=6
+ for word in '$(cat $1)'
+ '[' 6 == '' ']'
+ dict[$word]=7
+ for word in '$(cat $1)'
+ '[' 7 == '' ']'
+ dict[$word]=8
+ for word in '$(cat $1)'
+ '[' 8 == '' ']'
+ dict[$word]=9
+ for word in '$(cat $1)'
+ '[' 9 == '' ']'
+ dict[$word]=10
+ for word in '$(cat $1)'
+ '[' 10 == '' ']'
+ dict[$word]=11
+ for word in '$(cat $1)'
+ '[' 11 == '' ']'
+ dict[$word]=12
+ for word in '${!dict[@]}'
+ echo 0: 12 0: 12

【问题讨论】:

  • @steve 不,我自己也在学习 bash。我只想用 bash 解决这个问题
  • 使用调试跟踪是个好主意,现在看看你的调试跟踪显示给你什么。为什么它正在读取的每个单词将dict[$word]= 右侧的值增加1?这不是你想要的,不是吗?最后,您的调试循环也是一个好主意,但输出再次显示您的变量命名/分配/取消引用正在按您的预期工作。祝你好运。
  • 好的,我很欣赏你的尝试。请注意,$[ .... ] 没有按照您的预期进行,并且您可以将增量过程的代码减少到 (( dict[$word]++ ))(我认为这在 bash 中有效,我确信它在 ksh 中有效)。而且.. 最后,文件 i sort file file2 ... | uniq -c 中字数统计的老式 unix 解决方案。祝你好运。

标签: arrays bash


【解决方案1】:

使用declare -a dict 意味着每个键都被评估为一个数值,然后将其用作索引。如果你用文字来存储东西,那不是你想要的。请改用declare -A


另外,$[ ] 是一种非常过时的数学语法。即使是现代 POSIX sh 也支持 $(( )),您应该使用它来代替:

dict[$word]=$(( ${dict[$word]} + 1 ))

或者,利用仅限 bash 的数学语法:

(( dict[$word]++ ))

此外,使用for word in $(cat $1) 有几个方面的问题:

  • 它不引用$1,因此对于带有空格的文件名,它会将名称拆分为几个单词并尝试将每个单词作为单独的文件打开。要仅解决此问题,您可以使用 $(cat "$1")$(<"$1")(效率更高,因为它不需要启动外部程序 cat)。
  • 它尝试将文件中的单词扩展为 glob -- 如果文件包含 *,则当前目录中的每个文件都将被视为一个单词。

改为使用while循环:

while read -r -d' ' word; do
  if [[ -n ${dict[$word]} ]] ; then
    dict[$word]=$(( ${dict[$word]} + 1 ))
  else
    dict[$word]=1
  fi
done <"$1"

【讨论】:

  • 它有效。主要问题是“声明-A dict”。我从你的回答中学到了很多。非常感谢。
  • 虽然$((...))(算术扩展)是POSIX,但复合命令((...))不是。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-05
  • 1970-01-01
  • 1970-01-01
  • 2018-08-25
  • 1970-01-01
  • 2021-04-19
相关资源
最近更新 更多