这里四道shell的面试题,学习shell的童鞋用来练手很不错。
前三道比较简单,都是对文件内容进行,按要求输出结果。用sed,awk,while-read以及here document和regex即可实现。
难点和重点在第四道题,算法性比较强。可以使用多个循环实现的很简单,但效率会很低。环的数字很多时,要很长时间才能运算完成。这里通过一步步的改进算法,做到了性能最优。代码改进过程中用到了shell调试的一些技能,如shell x选项,trap, PS4等。
========== 题目一 =============
文件内容:
100
a 100
b -50
c -20
d -30
要求输出:
100
a 100
200
b -50
150
c -20
130
d -30
代码:
#!/usr/bin/env bash
# author: liguo
# sumary: ex01.sh
awk -F$'\t' '{
if(NR==1){
sum=$1;
next;
}
print sum;
print $0;
sum+=$2;
}' << EOF
100
a 100
b -50
c -20
d -30
EOF
========== 题目二 =============
===========题目四=======
文件内容:
123abc456
456def123
567abc789
789def567
要求输出:
456ABC123
123DEF456
789ABC567
567DEF789
代码:
#!/usr/bin/env bash===============题目三=============
# author: liguo
# sumary: ex02.sh
while read line;do
echo $line | tr [:lower:] [:upper:] | sed -r 's/([0-9]{3})([A-Z]{3})([0-9]{3})/\3\2\1/'
done << EOF
123abc456
456def123
567abc789
789def567
EOF
文件内容:
1.1.1.1 11
1.1.1.1 22
1.1.1.1 33
1.1.1.1 44
2.2.2.2 11
2.2.2.2 22
2.2.2.2 33
2.2.2.2 44
1.1.1.1 22
1.1.1.1 33
1.1.1.1 44
2.2.2.2 11
2.2.2.2 22
2.2.2.2 33
2.2.2.2 44
要求输出:
1.1.1.1 11 22 33 44
2.2.2.2 11 22 33 44
1.1.1.1 11 22 33 44
2.2.2.2 11 22 33 44
awk实现代码:
#!/usr/bin/env bash
# author: liguo
# sumary: ex03.sh
{
awk '{aa[$1]=length(aa[$1])>0 ? aa[$1]" "$2 : $2;}
END {
for(i in aa)
{
print i"\t"aa[i];
}
}'<< EOF
1.1.1.1 11
1.1.1.1 22
1.1.1.1 33
1.1.1.1 44
2.2.2.2 11
2.2.2.2 22
2.2.2.2 33
2.2.2.2 44
EOF
} | sort # lines output by descending by default, sort it by ascending here.
sed实现代码:
#!/usr/bin/env bash
# author: liguo
# sumary: ex03b.sh
# use variables to generate long regex for sed, let code more readable
key="([0-9\.]{7})"
sps="[[:space:]]+"
val="([0-9]{2})"
lne="$key$sps$val"
re="$lne$sps$lne$sps$lne$sps$lne"
sed -r "N;N;N;s/\n/ /g;s/$re/\1 \2 \4 \6 \8/g" << EOF
1.1.1.1 11
1.1.1.1 22
1.1.1.1 33
1.1.1.1 44
2.2.2.2 11
2.2.2.2 22
2.2.2.2 33
2.2.2.2 44
EOF
有1到100的数字序列围成一个环。计数器从1开始数,每数到12的整数倍,就将计数器指向的数字从环中剔除,问该环中最后剩下的数字是多少?100结果为81
代码:为了保证数字序列长度很大时, 程序的执行效率,对代码进行了多次优化,比如只用使用了一个循环;环中数字被移除后,左移后面的数字,减小序列,避免扫描数组空位置;防止数字溢出等。
#!/usr/bin/env bash
# author: liguo
# summary: ex05.sh
# usage: 0x05.sh [number]
# example:
# bash ex05.sh 10000
# The final left number is: 6746
# open debug options
#set -x
#export PS4="+[$LINENO:${FUNCNAME[0]}]"
#trap 'echo "n1=$n1, n2=$n2, n3=$n3, ni=$ni, aa[y-n3]=${aa[$[y-n3]]}, aa[y]=${aa[$y]}, y=$y"' DEBUG
# validate number count of the last outter loop
if [[ $1 =~ ^[0-9]+$ ]];then # test if $1 is a valid number.
n0=$1
else
n1=100
fi
let n1=n0 # the outter loop index uplimit of the outter loop
let n2=n1 # real time valid number count
let ni=0 # the counter, we need it to increases without restore to 1. But variable i will do.
for ((i=1;n1>1;i=i%n1+1));do
let ni++
if [ $[ni%12] -eq 0 ];then
let aa[i]=0
let n2--
let n3=n1-n2
# when n0 is large value, ni will be very large,
# it's very possible to overflow. So reset it to 0, it will more easy to calculate ni%12 too.
let ni=0
elif ((n1==n0));then # if it is the firt time of the out loop, fill array aa with value i.
let aa[i]=i
fi
if ((n2<n1 && aa[i]!=0));then
# remove some number that at the position of 12*n, now move later array elements left
# to fill these empty positons. Then it will loop fewer positions after loop index i restore to 1.
# This is important when variable n0 is a large number, such as 1000,000
if ((n3>0));then
let aa[i-n3]=aa[i]
fi
fi
# make i==n1, ensure outter loop index 'i' restart from 1 after operation 'i=i%n1+1'
if ((i==n1 && n2<n1));then
let n1=n2
let i=n1
let n3=0
fi
done
echo "The final left number is: ${aa[$n1]}"
# close debug options
#set +x
#export PS4="+"