这里四道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="+"