解析企业Shell面试题

一个同学问我一个问题,说有以下文件内容,要求输出为特定的格式。这里就献丑给出一个处理的方法吧,由于时间关系可能我的答案并不是最好的,但是我尽量将我的答案讲解明白,让你理解处理的方法。如果您有简单明了的处理方法请不啬赐教!


题目

文件内容如下:


2016-12-08       00:09        血战钢锯岭

2016-12-08       03:01        你的名字

2016-12-08       04:00        长城

2016-12-08       04:01        萨利机长

2016-12-09       07:35        神奇动物在

2016-12-09       09:24        湄公河行动

2016-12-09       10:59        我不是潘金莲

2016-12-09       12:43        海洋奇缘

2016-12-09       14:29        神奇四侠2015

2016-12-10       16:30        死侍

2016-12-10       16:31        加勒比海盗5:死

2016-12-10       16:36        三体

2016-12-10       18:04        阿凡达2

要求输出结果为:


2016-12-08

00:09        血战钢锯岭

03:01        你的名字

04:00        长城

04:01        萨利机长

2016-12-09

07:35        神奇动物在

09:24        湄公河行动

10:59        我不是潘金莲

12:43        海洋奇缘

14:29        神奇四侠2015

2016-12-10

16:30        死侍

16:31        加勒比海盗5:死

16:36        三体

18:04        阿凡达2

19:40        日落七次


参考答案

看到题目后,发现文件内容中的规律,源文件主要由年月日、时分和电影名称组成三列,而目标文件年月日主要是去重,每个日期只出现了一次后换行,将当日要上映电影的时分和电影名称按行显示出来。

因为是文件处理,首先我想到了sed来处理,使用sed加正则表达式将文件内容进行分组处理,然后去除重复的“年月日”,实现代码如下:


# sed -r  ‘s/([0-9]{4}-[0-9]{2}-[0-9]{2})(\t)(.*)(\t)(.*)/\1\n\3\t\5/‘ file  |sed -r ‘:1;N;s/^(\S+)((\n.*)*)\n\1$/\1\2/M;$!b1‘

2016-12-08

00:09        血战钢锯岭

03:01        你的名字

04:00        长城

04:01        萨利机长

2016-12-09

07:35        神奇动物在

09:24        湄公河行动

10:59        我不是潘金莲

12:43        海洋奇缘

14:29        神奇四侠2015

2016-12-10

16:30        死侍

16:31        加勒比海盗5:死

16:36        三体

18:04        阿凡达2

19:40        日落七次

#


答案剖析

首先,我们先看管道前的代码,主要是将“年月日”和“时分,电影名称”分成了两部分。我们看到sed的-r选项的意思是支持扩展的这规则表达式,类似grep的-P。其中([0-9]{4}-[0-9]{2}-[0-9]{2})(\t)(.*)(\t)(.*)是正则表达式和分组。

正则表达式是指用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。就是用某种模式去匹配一类字符串的一个公式。Sed中的正则如下表所示:


^


行的开始


$


行的结尾


.


一个字符


*


匹配0个或多个*前面的字符


[]


方括号中的所有字符


\


转义


{}


重复次数


()


分组,将匹配这个表达式的字符保存到一个临时区域(最多保存9个),它们可以用\1到\9来引用。


/./


匹配至少有一个字符


/../


匹配至少有两个字符的行


/^#/


匹配用#开头的行


/^$/


匹配空行


/}$/


匹配用}结尾的行(没有空格在后面)


/} *$/


匹配用}结尾的行(可以有空格在后面)


/[abc]/


匹配小写的a或b或c


/^[^abc]/


匹配开头不是小写的a或b或c

匹配该题目的正则表达式的含义如下:

·([0-9]{4}-[0-9]{2}-[0-9]{2}):匹配数字0-9重复4次,匹配年;匹配数字0-9重复2次匹配月和日,并使用()分组

·(\t):匹配制表符,如果你使用的空格分隔,直接匹配空格,可以使用(\ |\t)匹配空格或者制表符

·(.*):匹配任意字符

接下来解释演示/\1\n\3\t\5/的含义,为了减少篇幅,我复制一份文件名为file1,内容如下:


# cat file1

2016-12-08       00:09        血战钢锯岭

2016-12-08       03:01        你的名字

2016-12-09       10:59        我不是潘金莲

#

执行如下命令,结果表明\1代表分组1,自然\3和\5代表分组3和5


# sed -r "s/([0-9]{4}-[0-9]{2}-[0-9]{2})(\ |\t)(.*)(\  |\t)(.*)/\1/" file1

2016-12-08

2016-12-08

2016-12-09

#

那么\n代表什么呢?我们在现有命令上加上/n,结果表明\n代表换行,如下所示:


# sed -r "s/([0-9]{4}-[0-9]{2}-[0-9]{2})(\ |\t)(.*)(\  |\t)(.*)/\1\n/" file1

2016-12-08

2016-12-08

2016-12-09

#

\t不用解释了吧,完整的执行以下如下所示:


# sed -r "s/([0-9]{4}-[0-9]{2}-[0-9]{2})(\ |\t)(.*)(\ |\t)(.*)/\1\n\3\t\5/"  file1

2016-12-08

00:09        血战钢锯岭

2016-12-08

03:01        你的名字

2016-12-09

10:59        我不是潘金莲

# sed -r "s/([0-9]{4}-[0-9]{2}-[0-9]{2})(\ |\t)(.*)(\ |\t)(.*)/\1\n\3\t\5/"  file1 >>file2

继续看管道后的命令的含义,之后的命令信息量有点稍微大。


# sed -r ‘:1;N;s/^(\S+)((\n.*)*)\n\1$/\1\2/M;$!b1‘ file2

2016-12-08

00:09        血战钢锯岭

03:01        你的名字

2016-12-09

10:59        我不是潘金莲

#

主要有多行模式空间的操作命令N、D、P和sed脚本流程控制命令b、t。

多行模式空间(MultilinePattern Space):就是在模式空间中放置输入文件的多个行内容,操作多行模式空间的有N、D、P含义如下:

·N命令:是将下一行也输入到模式空间中,当前行与下一行之间插入一个’\n’,以下为示意图:

·D命令:仅删除Multiline Space中第一个’\n’之前的内容,如上图,即删除“The UnixOperating System”,而“Is A interestingSystem”仍然存在。同时,它使得脚本的控制流转到脚本文件的第一行,跳过该命令的后续命令。

·P命令:仅打印Multiline Space中第一个’\n’之前的内容,如上图,即仅打印“The UnixOperating System”。

我们看下N的范例,还是使用file1文件中的内容,命令执行结果如下:


# sed ‘N’ file1

2016-12-08       00:09        血战钢锯岭

2016-12-08       03:01        你的名字

2016-12-09       10:59        我不是潘金莲

#

看到结果后,有同学会说,这和Sed不加任何的选项和命令,执行的结果相同。如下所示:


# sed ‘‘ file1

2016-12-08       00:09        血战钢锯岭

2016-12-08       03:01        你的名字

2016-12-09       10:59        我不是潘金莲

#

使用肉眼咋一看真发现不了区别,首先我们回顾下sed的工作过程:

sed会先读取文本中的第一行,到模式空间,然后执行sed命令,处理完成后,将结果发送到屏幕上。sed每处理完一行就将其从模式空间中删除,接着会读取文本中的第二行,到模式空间,然后执行sed命令,处理完成后,将结果发送到屏幕上。重复此过程,直到文本中的最后一行,sed便结束运行。

了解sed的工作原理,我们发现没有使用N命令时候,sed依次将文本中的行读取到模式空间中,sed没有做任何的命令操作,他就直接显示到屏幕上了。

当使用N命令后,sed执行过程是sed会先读取文本中的第一行 “2016-12-08  00:09  血战钢锯岭$”到模式空间,然后执行sed命令N,模式空间中的第一行内容后追加第二行内容生成多行模式空间的第一行内容,多行模式空间变为“2016-12-08  00:09  血战钢锯岭\n2016-12-08  03:01  你的名字$”,处理完成后,将结果发送到屏幕上。sed继续向模式空间读取下一行内容,本例中就是第三行,然后再次追加下一行内容,生成多行模式空间中的第二行内容,以此类推。由于本例中第三行下没有内容,这时候执行N命令后就不会生成多行模式空间的第二行内容,所以模式空间中有“2016-12-09         10:59        我不是潘金莲”。处理完成后,将结果发送到屏幕上。

结果表明,我们使用N命令后,前两行输出的是多行模式空间的内容,最后一行是模式空间的内容。由于多行模式空间合并的第一行和第二行之间有\n,所以看到输出的格式没有变化,为了证明这个说法,我们将\n 替换成空格,如下所示:


# sed ‘N;s/\n/ /‘ file1

2016-12-08       00:09  血战钢锯岭 2016-12-08  03:01      你的名字

2016-12-09       10:59        我不是潘金莲

#

通常,sed是将编辑命令从上到下依次应用到读入的行上,N命令能够在一定程序上改变默认的执行流程,甚至利用N命令可以形成一个强大的循环处理流程。除此之外,其实sed还提供了分支命令(b)和测试(test)两个命令来控制流程,这两个命令可以跳转到指定的标签(label)位置继续执行命令。标签是以冒号开头的标记,标签名称可以自定义。例如:定义一个名称为:label标签,如下所示:


:label

command1

/pattern/b label

command2

当执行到/pattern/b top时,如果匹配pattern,则跳转到:label标签所在的位置,继续执行下一个命令command1。

上面的例子用到了分支命令,分支命令的跳转是无条件的。而与之相对的是测试命令,测试命令的跳转是有条件的,当且仅当当前行发生成功的替换时才跳转。

为了明白测试命令的用法,我们用它来实现file1中的内容:


# sed  -e ‘:1;s/2016/2017/;t1;‘  file1

2017-12-08       00:09        血战钢锯岭

2017-12-08       03:01        你的名字

2017-12-09       10:59        我不是潘金莲

#

我们定义了一个标签为:1,然后在最后利用测试命令跳转到该标签。可能,你会觉得这里也可以使用分支命令,但是事实上分支命令会导致死循环,因为在它里他没有结束的条件。

但是测试命令就不同了,这一点直到最后才体现出来。当最后一行被s/2016/2017/命令读入之后,2016替换成2017,此时ta继续跳转到最开头,因为模式空间中的2016已经全部被替换成2017,所以替换也不会发生。之前我们说过,当且仅当当前行发生成功的替换时测试命令才跳转。所以此时跳转不会发生,退出sed命令。

到此,你能看明白后半句的意思吗?欢迎留言!

欢迎使用微信关注“运维爱好者”公共号,第一时间了解本博客动态!

时间: 2024-10-12 09:24:00

解析企业Shell面试题的相关文章

2017最新企业Shell面试题及企业运维实战共30道案例

<跟老男孩学习Linux运维:Shell高级编程实战>一书第19章企业面试题分享. 答案: 1.答案在<跟老男孩学习Linux运维:Shell高级编程实战>第19章一书2016年年底前即将出版. 2.2016最新Shell视频http://edu.51cto.com/pack/view/id-546.html 第19章企业Shell面试题及企业运维实战案例 19.1 企业Shell面试题实战案例 19.1.1 企业Shell面试题1:批量生成随机字符文件名案例 使用for循环在/o

企业shell面试题:获取51CTO博客列表按时间倒序排序

企业shell面试题:获取51CTO博客列表倒序排序考试题 老男孩教育培训机构需求:需求入下: 请把http://oldboy.blog.51cto.com 地址中的所有博文,按照时间倒序列表如下: 2013-09-13 运维就是一场没有硝烟的战争 http://oldboy.blog.51cto.com/2561410/1296694 2016-04-17 运维人员写项目方案及推进项目的基本流程思路 http://oldboy.blog.51cto.com/2561410/1764820 附加

企业shell面试题及解答

1.面试题:使用for循环在/tmp目录下批量创建10个html文件,其中每个文件需要包含10个随机小写字母加固定字符串template,示例如下 aaesdffbnv_template.html 方法1: cd /tmpfor ((i=0;i<10;i++));do touch `echo $RANDOM | md5sum | sed 's/[^a-z]//g' | cut -c 1-10`_template.html;done 方法2: cd /tmp for i in `seq 10`;d

企业Shell面试题14:开发脚本入侵检测与报警案例

面试及实战考试题:监控web站点目录(/var/html/www)下所有文件是否被恶意篡改(文件内容被改了),如果有就打印改动的文件名(发邮件),定时任务每3分钟执行一次. 1.1问题分析 1)首先要说明的是,思考过程的积累比实际代码开发的能力积累更重要. 2)什么是恶意篡改,只要是未经过许可的改动都是篡改. 3)文件内容被改动了会有如下特征. ◎ 大小可能会变化 ◎ 修改时间会变化 ◎ 文件内容会变化,利用md5sum指纹校验 ◎ 增加或删除文件,比对每次检测前后的文件数量. 1.2参考解答

兄弟连 企业shell笔试题 16-31

企业实践题16:企业案例:写网络服务独立进程模式下rsync的系统启动脚本 例如:/etc/init.d/rsyncd{start|stop|restart} .要求:1.要使用系统函数库技巧.2.要用函数,不能一坨SHI的方式.3.可被chkconfig管理. 脚本1: #!/bin/bash pidfile="/var/run/rsyncd.pid"result=`ps aux|grep rsync|grep -v 'grep'` Rstatus(){ if [ $resultX

企业Shell面试题1:批量生成随机字符文件名案例

使用for循环在/oldboy目录下批量创建10个html文件,其中每个文件需要包含10个随机小写字母加固定字符串oldboy,名称示例如下: [[email protected]]# ls /oldboy apquvdpqbk_oldboy.html mpyogpsmwj_oldboy.html  txynzwofgg_oldboy.html bmqiwhfpgv_oldboy.html mtrzobsprf_oldboy.html  vjxmlflawa_oldboy.html jhjdcj

企业shell面试题:获取51CTO博客列表倒序排序考试题

#!/bin/sh PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin HTMLFILE=/home/oldboy/html HTTP=http://oldboy.blog.51cto.com/all/2561410 NUM=$(curl $HTTP |awk -F "[ /]" '/页数/ {print $(NF-3)}') [ -d $HTMLFILE ]||mkdir $HTMLFILE -p ech

linux系统运维企业常见面试题集合(三)

linux系统运维企业常见面试题集合(三) 01  写一个sed命令,修改/tmp/input.txt文件的内容,要求:(1) 删除所有空行:(2) 一行中,如果包含"11111",则在"11111"前面插入"AAA",在"11111"后面插入"BBB",比如:将内容为0000111112222的一行改为:0000AAA11111BBB2222 [[email protected]~]# cat -n /t

那些年我们一起做过的shell面试题(一)

企业真实shell面试题,一起来挑战把! 声明:如有雷同,纯属抄袭 1.开发一个守护进程脚本,每30秒执行一次,检查mysql主从同步 [[email protected] scripts]# cat check_mysql_slave_status.sh #!/bin/bash# date 2016-1-5# author tom# mail [email protected]# function check_mysql_slave_status# version 4.1.2 ####do i