shell 练习题集合(思路和解答)


##########################################################################################

https://www.centos.bz/2012/07/shell-script-exercises/

Q1:

Q1题目:

分析图片服务日志,把日志(每个图片访问次数*图片大小的总和)排行,也就是计算每个url的总访问大小明:本题生产环境应用:这个功能可以用于IDC网站流量带宽很高,然后通过分析服务器日志哪些元素占用流量过大,进而进行优化或裁剪该图片,压缩js等措施。


测试数据 access_log

59.33.26.105 �C �C [08/Dec/2010:15:43:56 +0800] “GET /static/images/photos/2.jpg HTTP/1.1″ 200 11299


输入要求:

本题需要输出三个指标: 【被访问次数】 【访问次数*单个被访问文件大小】 【文件名(带URL)】


Q1知识回顾:awk

awk 语法: [-F  field-separator]  '{pattern + action}'  input-file(s)

awk 把文件逐行读入,默认域分隔符是"空白键" 或 "tab 键",可以用 -F ' ' 修改;$0则表示所有域,$1表示第一个域,$n表示第n个域。


常见例子:

awk -F: 'BEGIN {count=0;print "[start] user count is:",count} {count=count+1;print $1;} END {print "[end]user count is:" count}' /etc/passwd

[start]user count is  0

root

...

[end]user count is  40


!!重要技巧

因为awk是以字符串格式来存储数字值,但又可以进行数值计算;

并且数组中的下标也同样视为字符串array["1"]=array[1]。特别的,还可以用字符串来作为下标,myarr["name"]="Mr. Whipple",print myarr["name"] 正常。这种数组叫做关联数组。


Q1思路:

分析http日志,用awk命令

access_log中的第七列为访问的文件(路径),第十列为返回数据大小即图片大小B.(补充:第九列为状态码)


Q1解答:

awk 'BEGIN{} {url[$7]++;size[$7]+=$10;} END{for (a in url) {print url[a],size[a]/1024 "K",a}}' /var/log/httpd/access_log

url记录单个url被访问次数,size统计总字节。

##########################################################################################

Q2:

Q2问题;

计算出1+2+3+..+100的结果。可以使用多种方法解答。


Q2知识回顾:

shell中的数学运算不同于$变量,

如:var=1+1 echo $var 输出的结果是1+1;

如:var=1 var=$var+1 echo $var 输出结果是1+1

数组运算不需要带$


整数运算:let,(()),$[],expr

浮点数运算:管道方法bc,awk,


for语法:for (()) do done


Q2解答1:

在有bc命令下:

seq -s “+” 100 | bc


Q2解答2:

sum=0
for ((i=0;i<=100;i++)) 或者 for i in {1..100}
do
    ((sum+=i))
done
echo $sum


Q2解答3:

sum=0
i=0
while [ i -le 100 ]
do
    ((sum+=i))
done
echo $sum

##########################################################################################

Q3:

Q3题目:

分析网站日志,找出在一分钟内打开网页超过60次的ip(排除图片,js和css等静态元素),并用iptables禁止其访问。加入crontab使脚本每分钟执行一次。


Q3知识回顾:

egrep = grep -E 可以使用基本的正则表达外, 还可以用扩展表达式. 注意区别.扩展表达式:

egrep 用以下的字符的特殊功能(如果需要匹配一下字符,可以“\”转义

+ 匹配一个或者多个先前的字符, 至少一个先前字符.

? 匹配0个或者多个先前字符.

a|b|c 匹配a或b或c

() 字符组, 如: love(able|ers) 匹配loveable或lovers.

(..)(..)\1\2 模板匹配. \1代表前面第一个模板, \2代第二个括弧里面的模板.

x{m,n} =x\{m,n\} x的字符数量在m到n个之间.


Q3思路:

用当前时间的前后一分钟来分析 date +%H%M%S 和 date -d "1 minute ago" +%H%M%S

日志记录的时间在一分钟的范围内,ip_count[$1]++

用egrep -v .(gif|jpg|jpeg|png|css|js) 排除

awk 命令 -F "[ :]" 提取出%H%M%S,这里有比较特殊的是IPV6地址::1,得再筛选掉。

另外注意的是:在加入iptables是要加判断

Q3解答:

now=`date +%H%M%S`
a_min_ago=`date -d "1 minute ago" +%H%M%S`
badip=`tail -n 10000 /var/log/httpd/access_log |egrep -v "^::" |egrep -v ".(gif|jpg|jpeg|png|css|js)" | awk -F "[ :]" 'BEGIN{
    n='"$now"';
    a='"$a_min_ago"';
}
{
    t=$5$6$7;
    if (t>=a && t<=n){
    ip_count[$1]++;
}
END{
    for (ip in ip_count){
        if (ip_count[ip]>10){
        print ip;
            }
        }
    }
'`
echo $badip
if [ ! -z $badip ];
then
    for ip in $badip
do
    if [ -z "`/sbin/iptables -nL |grep $ip`" ];
    then
    /sbin/iptables -I INPUT -s $ip -j DROP
    fi
done
fi

##########################################################################################

Q4:

Q4题目:

Name,Team,First Test, Second Test, Third Test

Tom,Red,5,17,22

Joe,Green,3,14,22

Maria,Blue,6,18,21

Fred,Blue,2,15,23

Carlos,Red,-1,15,24

Phuong,Green,7,19,21

Enrique,Green,3,16,20

Nancy,Red,9,12,24


计算每个人的平均成绩,每次测试的平均成绩和每组队的平均成绩。如果某次成绩为负数,则表示此人错过了测试,那计算平均成绩时排除此人再计算。输出的结果如下表,在名字的列表中,名字是10个宽度且左对齐(提示printf中使用%-10s格式),而平均值是7个字符宽度,右边两个右对齐的小数。



Q4答案:

#!/bin/bash
awk -F "," 'BEGIN{
    printf "%-10s %s\n","Name","Average";
    printf "%-10s %s\n","----","-------";
}
NR>1{
    for (i=3;i<=5;i++){
        if ($i>0){
        name_count[$1]++;
        name_sum[$1]+=$i;
        test_count[i-2]++
        test_sum[i-2]+=$i
        team_count[$2]++;
        team_sum[$2]+=$i;
        }
    }
    printf "%-10s %7.2f\n",$1,name_sum[$1]/name_count[$1];
}
END{
    print "------------------"
    for (j=1;j<=3;j++){
        print "Average for Test "j": " test_sum[j]/test_count[j]
}
print "------------------"
for (t in team_count){
    print "Average for "t" Team: " team_sum[t]/team_count[t];
}
}' teamlist

##########################################################################################

Q5:

Q5题目:

传入至少三个数字参数到脚本file,并计算出最大,最小,平均值。需要判断传入的数字是否足够,否则输出警告信息。平均值保留两位小数。


Q5答案:

#!/bin/bash
n=$#
if [ $n -lt 3 ];then
    echo "parameter must be at least 3"
    exit 1
fi
max=$1
min=$1
sum=$1
shift
while [ $# -gt 0 ]
do
    ((sum+=$1))
    if [ $1 -gt $max ];then
        max=$1
    fi
    if [ $1 -lt $min ];then
        min=$1
    fi
    shift
done
echo "max number is: " $max
echo "min number is: " $min
ave=`echo "$sum $n" |awk '{printf("%.2f", $1/$2)}'`
echo "ave number is: " $ave

##########################################################################################

Q6:

Q6题目:

有一列数字如下:

第1次:1

第2次:2

第3次:3

第4次:5

第5次:8

第6次:13

第7次:21

第8次:34

第9次:55

第10次:89

第11次:144

写出100次的数是什么。


Q6答案:


给的参考答案在92的时候出现复数。

自己的答案:

#!/bin/bash
awk 'BEGIN{
    num[1]=1;
    num[2]=2;
    for (i=3;i<=100;i++){
    num[i]=num[i-1]+num[i-2]
}
print num['"$1"']
}'

##########################################################################################

Q7:

Q7题目:

文件内容如下:

123abc456

456def123

567abc789

789def567

要求输出:

456ABC123

123DEF456

789ABC567

567DEF789


Q7答案:

自己的答案;

#!/bin/bash
awk '{
    a=substr($1,1,3);
    b=toupper(substr($1,4,3));
    c=substr($1,7,3);
    printf c
    printf b
    printf a
    printf "\n"
}' q7data

##########################################################################################

Q8:

Q8问题:

1 2 4 8

5 8 0 2

3 1 8 3

7 0 2 7

3 9 1 0

3 2 7 4

把第2列和第3列的和作为新的第5列,第5列的平均值为avg5,求第5列中大于avg5的行数。


Q8答案:

a=`cat data |wc -l`
awk 'BEGIN{
    i=1;
    j=1;
    count=0;
    count2=0;
}
{
    x=$2+$3
    num[i]=x
    i++
    sum+=x
    print NR
}
END{
    avg5=sum/'"$a"'
    for (j=1;j<=6;j++){
    if (num[j]>avg5){
    count++
}
}
for (k in num){
    if (num[k]>avg5){
    count2++;
}
}
print "The sum: " sum
print "The average: " avg5
print "The counter: " count
print "The counter2: " count2
}' data

##########################################################################################

Q9:

Q9问题:

stu.txt

100:张三:男:计算机  

101:张红:女:文秘  

102:张飞:男:体育  

103:张婷:女:英语  

104:张海洋:男:机电  


该文件是所有学生的信息,每个学生存储一行信息,信息格式如下:学号:姓名:性别:专业    如(100:张三:男:计算机)设计一个shell,名称为stu.sh,该shell完成如下功能:

1)当输入stu.sh时,列出所有记录内容

2)当输入 stu.sh -a 100:张三:男:计算机    时,首先判断100记录是否存在,如果不存在,则把该信息写入文件,如果存在,则给出提示,并输出文件中学号为100的该行信息

3)当输入 stu.sh -d 100时,首先判断100记录是否存在,如果不存在,给出提示,如果存在,则提示用户确认是否要删除记录,如用户输入y或者yes,则删除文件中学号为100的该行信息,如果用户输入n或no时,则不做删除操作

4)当输入 stu.sh -s 100时,首先判断100记录是否存在,如果不存在,给出提示,如果存在,则输出文件中学号为100的该行信息

5)当用户输入的选项不正确时,给出错误提示,并输入该shell的用法

Q9答案:

data="stu.txt";
sid="学号";
sname="姓名";
ssex="性别";
smajor="专业";
help(){
  echo "不加参数,显示所有记录";
  echo "-a 添加记录";
  echo "-d 删除记录";
  echo "-s 搜索记录";
}
if [ $# -eq 0  ];
then
  printf "%-s\t%-s\t%-s\t%-s\n" $sid $sname $ssex $smajor;
  #cat $data |awk -F ":" '{printf("%-s\t%-s\t%-s\t%-s\n",$1,$2,$3,$4);}';二选一
  cat $data|tr ':' '\t';
  exit;
fi
case $1 in 
-a)
  if ! grep -q $2 $data 2>&1;
  then
        echo $2>>$data;
        exit;
  else
        echo "存在";
        printf "%-s\t%-s\t%-s\t%-s\n" $sid $sname $ssex $smajor;
        echo $2|tr ':' '\t';
  fi
;;
-d)
  if ! grep -q $2 $data 2>&1;
  then
        echo "记录不存在。。";
        exit;
  else
        read -p "确定要删除?(y/n)" confirm;
        if [ $confirm == "y" -o $confirm == "yes" ];
        then
                sed -i "/$2/d" $data 2>&1;
        elif [ $confirm == "n" -o $confirm == "no" ];
        then
                echo "用户取消";
        else
                echo "错误的输入";
        fi
  fi
;;
-s)
  if ! grep -q $2 $data 2>&1;
  then
        echo "记录不存在。。";
        exit;
  else
        printf "%-s\t%-s\t%-s\t%-s\n" $sid $sname $ssex $smajor;
        #sed -n "/$2/p" $data |tr ':' '\t';
        grep $2 $data|tr ':' '\t';
  fi
;;
*)
  help
;;
esac

##########################################################################################

Q10:

Q10问题:

vat 1.txt

1 aa

2 bb

3 ee

4 ss


cat 2.txt

1 ab

2 cd

3 ad

4 bd

5 de


合并后的结果为:

1 ab aa

2 cd bb

3 ad ee

4 bd ss

5 de


Q10思路:

处理单个文件时,NR=FNR

处理多个文件时,按顺序处理。NR的行数会继续+,而FNR重新从1开始


Q10解答:

awk 'NR==FNR{a[$1]=$2}NR>FNR{print $0,a[$1]}'  1.txt  2.txt

等同于:

awk ‘BEGIN{
}
{
if (NR==FNR){
    a[$1]=$2
} 
if (NR>FNR){
    print $0,a[$1]
}
}
END{
}’ 1.txt 2.txt

##########################################################################################

Q11:

Q11题目:

编写shell脚本,将/usr/local/test目录下大于100k的文件转移到/tmp目录下。


Q11答案:

#!/bin/bash
for file in `ls /home/linpeisong`
do
    if [ -f /home/linpeisong/$file ];then
        echo /home/linpeisong/$file
        if [ `ls -l  /home/linpeisong/$file |awk '{print $5}'` -gt 100000 ];then
        cp /home/linpeisong/$file /tmp/
        fi
    fi
done


##########################################################################################

Q12:

Q12问题:

统计英语作文中单词频率,按单词,次数,降序排序


Q12思路:

用awk,gsub去掉符号,数组统计i


Q12答案:

#!/bin/bash
awk 'BEGIN{
}
{
# NF表示浏览记录域的个数
    gsub(/[.=,:;!?(){}]/, "")  
    for(i=1;i<=NF;i++){
    count[$i]++
    }
}
END{
    for(word in count){
    print word,count[word] | "sort -nr -k 2"
    }
}' file.txt


##########################################################################################

Q13:

shell计时,毫秒

START=`date +%s%N`
END=`date +%s%N`
time=`expr $((END-START)) / 1000000
echo $time"ms"


来自LeetCode的几道题目

##########################################################################################

Q14: Valid Phone Numbers


Given a text file file.txt that contains list of phone numbers (one per line), write a one liner bash script to print all valid phone numbers.


You may assume that a valid phone number must appear in one of the following two formats: (xxx) xxx-xxxx or xxx-xxx-xxxx. (x means a digit)


You may also assume each line in the text file must not contain leading or trailing white spaces.


For example, assume that file.txt has the following content:


987-123-4567

123 456 7890

(123) 456-7890

Your script should output the following valid phone numbers:

987-123-4567

(123) 456-7890


Q14思路:用sed,grep,awk匹配即可


Q14答案:(16ms)

awk "/^([0-9]{3}-)[0-9]{3}-[0-9]{4}$|^(\([0-9]{3}\)) [0-9]{3}-[0-9]{4}$/" file.txt


##########################################################################################

Q15: Tenth Line


How would you print just the 10th line of a file?


For example, assume that file.txt has the following content:


Line 1

Line 2

Line 3

Line 4

Line 5

Line 6

Line 7

Line 8

Line 9

Line 10

Your script should output the tenth line, which is:

Line 10


Q15思路:可以用head匹配第十行(不过得先做判断);或者用awk直接匹配


Q15解答1: (12ms)

#!/bin/bash
line_number=$(cat file.txt |wc -l)
if [ $line_number -lt 10 ];then
    echo ""
else 
    head -n 10 file.txt |tail -n 1
fi


Q15解答2: (16ms)

awk 'NR==10' file.txt


##########################################################################################

Q16: Word Frequency


Write a bash script to calculate the frequency of each word in a text file words.txt.


For simplicity sake, you may assume:


words.txt contains only lowercase characters and space ' ' characters.

Each word must consist of lowercase characters only.

Words are separated by one or more whitespace characters.

For example, assume that words.txt has the following content:


the day is sunny the the

the sunny is is

Your script should output the following, sorted by descending frequency:

the 4

is 3

sunny 2

day 1


Q16思路:可以利用shell中的字符数组保存词和对应词的次数,awk编程


Q16解答:(8ms)

#!/bin/bash
awk 'BEGIN{
}
{
    for(i=1;i<=NF;i++){
    count[$i]++
    }
}
END{
    for(word in count){
    print word,count[word] | "sort -nr -k 2"
    }
}' words.txt


##########################################################################################

Q16: Transpose File


Given a text file file.txt, transpose its content.


You may assume that each row has the same number of columns and each field is separated by the ' ' character.


For example, if file.txt has the following content:


name age

alice 21

ryan 30

Output the following:


name alice ryan

age 21 30


Q16思路:awk,数组


Q16解答:(40ms)

#!/bin/bash
awk '{
    for(i=1;i<=NF;i++){
    a[NR,i]=$i
    }
}
END{
    for(i=1;i<=NF;i++){
        for(j=1;j<=NR;j++){
            if(j==NR){
                printf a[j,i]"\n"
            }
            else{
                printf a[j,i]" "
            }
        }    
    }
}' file.txt


你可能感兴趣的:(shell,awk,练习题)