我有一个源代码来查找文件中的多个单词和字符:
#!/bin/bash
w=0
cc=0
for i in `cat $1`
do
j=$i
echo $j
w=$(($w+1))
c=`expr $j:'.*'`
cc=$(($cc+$c))
done
echo "no of characters" $cc
echo "no of words" $w
但是当我在终端中运行它时,显示以下错误消息^ ./countWordChar 1.c hello ./countWordChar: line 10: 0+hello:.*
: 表达式中的语法错误(错误标记是":.*"
) 字符数 0 单词数 1
代码中的第 10 行是cc=$(($cc+$c))
.显然,c 变量的值不是单词的字符数,而是单词本身。
我的1.c文件内容是这样的:
hello world
hello
代码有什么问题吗?
附言。我知道有内置命令可以计算文件中的字符数,但我必须根据我的任务使用以前的代码。
答案1
该expr
实用程序将其参数解析为表达式。运算符必须作为独立参数出现。
expr "$j" : '.*'
上面expr
传递了 4 个参数: 、 、和expr
的内容。假设 的内容不是or (或类似某些实现的东西),将在其下作为模式匹配运算符应用于 的内容。$j
:
.*
$j
(
!
length
expr
:
$j
为了使其更加健壮,您需要:
expr " $j" : '.*' - 1
(以空格开头的第二个参数不能被识别为 expr 运算符,因此可以解决上述问题)。
和
expr $j:'.*'
这将是两个参数(以及后面expr
的内容(假设不包含空白或通配符,请参见下文))。由于只看到一个参数(除了命令名称之外),因此没有请求任何操作,这只是一个仅回显的字符串参数。$j
:.*
$j
expr
expr
现在,您的代码还存在许多其他问题:
变量扩展和命令替换($((...))
或`...`
您使用的已弃用的形式),当未引用的经历时split+glob
。您确实希望split
该部分的部分`cat $1`
(应该是$(cat < "$1")
)将其拆分为单词,但不是 glob 部分,否则会将*
单词扩展到当前目录中的文件列表中;所有其他变量扩展都应该被引用(在赋值中不是必需的,但引号不会造成损害)。
所以应该是:
w=0 c=0
set -f # disable glob
for i in $(cat < "$1"); do
printf '%s\n' "$i"
w="$((w + 1))"
c="$(expr " $i" : '.*' + "$c" - 1)"
done
答案2
该expr $j:'.*'
命令expr
正在接收一 (1) 个参数。
该命令expr
无法理解这一点。
该命令expr
需要清楚地分隔每个参数:
expr "$j" ":" '.*'
这将是为命令提供的三 (3) 个参数expr
。"
周围的引号:
并不是真正需要的。最好在字符串之前使用空格$j
以避免一些误解,如下所示:
expr " $j" : '.*'
这将使您的脚本类似于:
#!/bin/dash
w=0 cc=0
for i in `cat $1`; do
echo "$j"
w=$(($w+1))
c=`expr " $i" : '.*'`
cc=$((cc+c))
done
echo "no of characters" $cc
echo "no of words" $w
但这更像是一个破折号脚本而不是一个 bash 脚本(这就是你标记问题的方式)。
简化的 bash 脚本如下所示:
#!/bin/bash
w=0 cc=0
for i in $(< $1)
do
((w++))
cc=((cc+${#i}))
done
echo "no of characters" "$cc"
echo "no of words" "$w"
相当于但稍快$(< $1)
一些$(cat $1)
。要增加 w,使用 更短w++
。为了计算字符数,我们可以使用$i
as的长度${#i}
。
或者甚至更短:
#!/bin/bash
w=0 cc=0
for i in $(< $1)
do (( w++ , cc += ${#i} ))
done
printf "no of characters %s\nno of words %s\n" "$cc" "$w"
通过使用 bash(从 2.04-devel 及更高版本)逗号,
运算符并使用 cc += ${#i}
相当于cc = cc + ${#i}
.