Shell编程练习题精选及答案

时间:2021-09-05 00:09:15
这里四道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       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="+"