一道有意思的 shell 脚本面试题
前几天面试的时候,有一道 shell
脚本题,让我印象深刻,倒不是多难,但是对于我来说,有点不太简单,回去的路上,我苦思冥想,也想不出来思路,题目大致是这样的:
有这么一个表,内容大概是这样,计算每个人的总成绩。
我回去之后,找了很多资料,需要用到 awk
这个命令,但是这个命令我实在不熟啊,于是请教我的朋友,他给我提供了两个思路:
思路一、
将每行的最后一列的成绩拿出来,保存到每个不同的变量上,然后需要计算的时候直接相加就可以了。
代码如下:
ax=`cat test.log |grep a|grep x|awk '{pring $3}'`
bx=`cat test.log |grep b|grep x|awk '{pring $3}'`
cx=`cat test.log |grep c|grep x|awk '{pring $3}'`
ay=`cat test.log |grep a|grep y|awk '{pring $3}'`
by=`cat test.log |grep b|grep y|awk '{pring $3}'`
cy=`cat test.log |grep c|grep y|awk '{pring $3}'`
xm=$($ax+$ay)
xh=$($bx+$by)
xb=$($cx+$cy)
echo '小明的总成绩为:'$xm
echo '小红的总成绩为:'$xh
echo '小白的总成绩为:'$xb
但是我想到,如果是这样的话,就太固定了,但凡随便加上一行,这个脚本就行不通了,于是有了思路2
思路二、
使用循环,定义一个记录行数的变量
i
,还有各自的人物变量xm(小明)、xh(小红)、xb(小白)
,每次读取一行,使i
加1
,然后将最后一列取出来,赋值给各自的人物变量,当循环到第三行,此时i=3
,赋值完成后, 将i
置为0
,这时再次进入循环,第四行的数据还是小明
的,依次类推,然后继续叠加,最后计算一下结果就可以了。
代码如下:
xm=0
xh=0
xb=0
i=0
cat test.log |while read line
do
((i++))
z=`echo $line|awk '{print $3}'`
if [ i=1 ]
then
xm=$((xm+z))
elif [ i=2 ]
then
xh=$((xh+z))
elif [ i=3 ]
then
xb=$((xb+z))
((i=0))
fi
done
echo '小明的总分是:'$xm
echo '小红的总分是:'$xh
echo '小白的总分是:'$xb
但是我想到,这样的话还是有点固定,如果人物位置变化一下,或者在加上一个人的话,那结果不就乱了么?
不过如果单纯解题的话,这两个思路还是没有任何问题的,还是得学习一下。
但是,我就比较较真,我在想,有没有比较完美的解题思路呢?
于是我开始查资料,学习、了解 awk
这个命令,就有了这版在我认为可以满足要求的思路:
思路三、
1、先将每行的第一列取出来,就是人名,因为要计算人物的总成绩嘛,所以不用考虑科目是什么
2、然后把结果赋值给一个变量,但是如果将awk
结果直接赋值的话,是一个字符串,不是我想要的数组,所以需要进行分割
3、将分割的结果赋值给一个新的数组变量,但是由于人物有重复的,所以需要去重,想法就是对新数组进行循环,把重复的数据删除,那剩下的就是不重复的
4、使用awk
命令,根据去重后的人名,计算这个人所有成绩的和,然后输出
list_old=`awk '{a=a$1" "};END {print a}' test.txt`
array=(${list_old// / })
array_len=${#array[*]}
for ((i=0;i<$array_len;i++))
do
for ((j=$array_len-1;j>i;j--))
do
if [[ ${array[i]} = ${array[j]} ]]
then
unset array[i]
fi
done
done
for x in ${array[*]}
do
result=`awk '/'$x'/ {sum += $3};END {print sum}' test.txt`
echo $x'的成绩是:'$result
done
这一套是我目前能想到的最好的解决办法,如果重新加上一个人,或者把顺序打乱,结果都是没问题的