[TOC]
Shell
Shell 脚本是什么?
一个 Shell 脚本是一个文本文件,包含一个或多个命令。作为系统管理员,我们经常需要使用多个命令来完成一项任务,我们可以添加这些所有命令在一个文本文件(Shell 脚本)来完成这些日常工作任务。
什么是默认登录 Shell ?
在 Linux 操作系统,"/bin/bash"
是默认登录 Shell,是在创建用户时分配的。
使用 chsh 命令可以改变默认的 Shell 。示例如下所示:
1 | ## chsh <用户名> -s <新shell> |
在 Shell 脚本中,如何写入注释?
注释可以用来描述一个脚本可以做什么和它是如何工作的。每一行注释以 #
开头。例子如下:
1 |
|
语法级
可以在 Shell 脚本中使用哪些类型的变量?
在 Shell 脚本,我们可以使用两种类型的变量:
系统定义变量
系统变量是由系统系统自己创建的。这些变量通常由大写字母组成,可以通过
set
命令查看。用户定义变量
用户变量由系统用户来生成和定义,变量的值可以通过命令
"echo $<变量名>"
查看。
Shell脚本中 $? 标记的用途是什么?
在写一个 Shell 脚本时,如果你想要检查前一命令是否执行成功,在 if
条件中使用 $?
可以来检查前一命令的结束状态。
如果结束状态是 0 ,说明前一个命令执行成功。例如:
1
2
3
4root@localhost:~## ls /usr/bin/shar
/usr/bin/shar
root@localhost:~## echo $?
0如果结束状态不是0,说明命令执行失败。例如:
1
2
3
4root@localhost:~## ls /usr/bin/share
ls: cannot access /usr/bin/share: No such file or directory
root@localhost:~## echo $?
2
Bourne Shell(bash) 中有哪些特殊的变量?
下面的表列出了 Bourne Shell 为命令行设置的特殊变量。
1 | 内建变量 解释 |
如何取消变量或取消变量赋值?
unset
命令用于取消变量或取消变量赋值。语法如下所示:
1 | ## unset <变量名> |
Shell 脚本中 if
语法如何嵌套?
1 | if [ 条件 ] |
在 Shell 脚本中如何比较两个数字?
在 if-then
中使用测试命令( -gt
等)来比较两个数字。例如:
1 |
|
Shell 脚本中 case
语句的语法?
基础语法如下:
1 | case 变量 in |
Shell 脚本中 for
循环语法?
基础语法如下:
1 | for 变量 in 循环列表 |
Shell 脚本中 while
循环语法?
如同 for
循环,while
循环只要条件成立就重复它的命令块。
不同于 for
循环,while
循环会不断迭代,直到它的条件不为真。
基础语法:
1 | while [ 条件 ] |
do-while 语句的基本格式?
do-while
语句类似于 while
语句,但检查条件语句之前先执行命令(LCTT 译注:意即至少执行一次。)。下面是用 do-while
语句的语法:
1 | do |
Shell 脚本中 break 命令的作用?
break
命令一个简单的用途是退出执行中的循环。我们可以在 while
和 until
循环中使用 break
命令跳出循环。
Shell 脚本中 continue 命令的作用?
continue
命令不同于 break
命令,它只跳出当前循环的迭代,而不是整个循环。continue
命令很多时候是很有用的,例如错误发生,但我们依然希望继续执行大循环的时候。
如何使脚本可执行?
使用 chmod 命令来使脚本可执行。例子如下:chmod a+x myscript.sh
。
#!/bin/bash 的作用?
#!/bin/bash
是 Shell 脚本的第一行,称为释伴(shebang)行。
- 这里
#
符号叫做 hash ,而!
叫做 bang。 - 它的意思是命令通过
/bin/bash
来执行。
如何调试 Shell脚本?
- 使用
-x'
数(sh -x myscript.sh
)可以调试 Shell脚本。 - 另一个种方法是使用
-nv
参数(sh -nv myscript.sh
)。
如何将标准输出和错误输出同时重定向到同一位置?
- 方法一:
2>&1 (如## ls /usr/share/doc > out.txt 2>&1 )
。 - 方法二:
&> (如## ls /usr/share/doc &> out.txt )
。
在 Shell 脚本中,如何测试文件?
test 命令可以用来测试文件。基础用法如下表格:
1 | Test 用法 |
在 Shell 脚本如何定义函数呢?
函数是拥有名字的代码块。当我们定义代码块,我们就可以在我们的脚本调用函数名字,该块就会被执行。示例如下所示:
1 | $ diskusage () { df -h ; } |
如何让 Shell 就脚本得到来自终端的输入?
read 命令可以读取来自终端(使用键盘)的数据。read 命令得到用户的输入并置于你给出的变量中。例子如下:
1 | ## vi /tmp/test.sh |
如何执行算术运算?
有两种方法来执行算术运算:
- 1、使用 expr 命令:
## expr 5 + 2
。 - 2、用一个美元符号和方括号(
$[ 表达式 ]
):test=$[16 + 4] ; test=$[16 + 4]
。
编程题
判断一文件是不是字符设备文件,如果是将其拷贝到 /dev
目录下?
1 |
|
添加一个新组为 class1 ,然后添加属于这个组的 30 个用户,用户名的形式为 stdxx ,其中 xx 从 01 到 30 ?
1 |
|
编写 Shell 程序,实现自动删除 50 个账号的功能,账号名为stud1 至 stud50 ?
1 |
|
写一个 sed 命令,修改 /tmp/input.txt
文件的内容?
要求:
- 删除所有空行。
- 一行中,如果包含 “11111”,则在 “11111” 前面插入 “AAA”,在 “11111” 后面插入 “BBB” 。比如:将内容为 0000111112222 的一行改为 0000AAA11111BBB2222 。
1 | [root@~]## cat -n /tmp/input.txt |
一、工作中你都写过什么脚本?
1、监控脚本(监控系统、监控服务、监控硬件信息、监控性能、安全监控等)
2、系统初始化脚本(创建目录,创建账户,安装软件包,设置权限,修改内核参数等)
一键部署(源码安装脚本)
3、备份脚本(自动备份数据库,备份网站数据,备份日志,备份配置文件等)
4、日志分析脚本(分析日志数据,汇总并统计相关信息,如 PV、UV 等)
二、如何获取一个文件每一行的第三个元素?
# awk ‘{print $3}’ 文件名
备注:awk 支持按列输出,通过内置变量$1,$2,$3…可以单独显示任意列,默认列是以
空格或 Tab 缩进为分隔符,也可以使用-F 选项指定其他分隔符。
三、 shell 函数能解决什么实际问题?
定义函数的格式:
function 函数名{
代码块
}
函数名(){
代码块
}
使用函数可以避免代码重复
使用函数可以将大的工程分割为若干小的功能模块,代码的可读性更强
四、使用 awk 统计 httpd 访问日志中每个客户端 IP 的出现次数?
# awk ‘{ip[$1]++}END{for(i in ip){print ip[i],i}}’ /var/log/httpd/access_log
备注:定义数组,数组名称为 ip,数字的下标为日志文件的第 1 列(也就是客户端的 I
P 地址)
,++的目的在于对客户端进行统计计数,客户端 IP 出现一次计数器就加 1。END
中的指令在读取完文件后执行,通过循环将所有统计信息输出。
4、哪些方式可以将标准输出和错误输出重定向到文件?
答案:
# 命令 &> 文件名
# 命令 > 文件名 1 2> 文件名 2
# 命令 > 文件名 2>&1
# 命令 &>> 文件名
# 命令 >> 文件名 1 2>> 文件名 2
# 命令 >> 文件名 2>&1
五、正则表达式符号: *、+、?、[]、[^]、{n}分别代表什么含义?
*表示匹配前面的字符出现了任意次(包括 0 次)
+表示匹配前面的字符出现了至少 1 次(1 次或多次)
?表示匹配前面的字符出现了 0 次或 1 次
[]表示集合,匹配集合中的任意单个字符
[^]表示对集合取反
{n}表示精确匹配前面的字符出现了 n 次
六、shell 中对变量字串进行截取的方式有哪些?
# echo ${变量名:开始位置:长度} #注意,起始位置从 0 开始
# expr substr $变量名 #注意,起始位置从 1 开始
# echo $变量名 |
开始位置 长度
cut -b 开始位置-结束位置 #注意,起始位置从 1 开始
\7. 使用 sed 命令如何将文件中所有的大写字母 Q 转换为小写字母 q?
答案:
# sed -i ‘s/Q/q/g’ 文件名
七、编写脚本,用户输入密码,脚本判断密码是否正确,输入正确则提示正确,连续输入错误密码 3 次,则报警?
1 | vim test.sh |
八、编写脚本,自动生成一个 8 位随机密码?
答案:
1 | vim test.sh |
九、递归函数,遍历目录
递归遍历目录
通过定义递归函数 files来实现
Shell也可以实现递归函数,就是可以调用自己本身的函数。在Linux系统上编写Shel脚本的时候,经常需要递归遍历系统的目录,列出目录下的文件和目录,逐层递归列出,并对这些层级关系进行展示。具体的实现过程如下所示。
1 | function list files() |
十、shell实现nginx日志自动切割
脚本思路【按天分割日志】
a、获取昨天的日期(date -d yesterday +%Y%m%d),用来作为分割后日志的名称
b、将源日志文件移动到新的nohuplogs文件夹里,并按时间重命名
c、在源日志文件夹(logs)里新建默认日志文件(access.log)
d、给nginx一个信号量,重新打开日志
f、设置一个定时任务,定时执行日志切割的脚本
操作步骤
a、新建日志分割的文件夹nohuplogs(mkdir /jboss/nginx/nohuplogs)
b、编写脚本,暂且命名为:splitlogs.sh吧,脚本内容如下:
1 | LOGPATH=/jboss/nginx/logs/access.log |
`
c、配置定时任务 crontab -e,如下图,增加日志分割的脚本,每天晚上23点59分切割
59 23 * * * sh /jboss/nginx/splitlogs.sh
十一、冒泡算法
请结合使用冒泡排序方法把 123.txt 文件中的数字按照降序排序输出在一行当中,并要求没有重复数字。(20分)
cat 123.txt
1 4 7 9
2 5 8 3
3 6 9 7
1 | #!/bin/bash |
###主体代码
1 | list=`for i in $(cat 123.txt); do echo $i; done | sort -n | uniq` |
echo “排序后数组为:$result”
监控网站状态是否正常,异常发邮件
题目要求:
写一个shell脚本,通过curl -I 返回状态码来判定所访问的网站是否正常,比如当代码状态200,才算正常
写一个发邮件的脚本
习题分析:
1、关键问题,截取出代码状态
2、在写出该shell脚本时,应该先在命令下面使用curl -I http://www.51xit.top/命令测试,然后通过awk截取到状态码
3、写发邮件的脚本,用的是sendEmail。生产环境有配套的模板
4、判断和发邮件关联
curl -I http://www.51xit.top/
我们抓包会有交互信息 200
###创建触发器及邮件报警测试##
【安装邮件组件】
[root@tang ~]# wget http://caspian.dotconf.net/menu/Software/SendEmail/sendEmail-v1.56.tar.gz
[root@tang ~]# tar -zxvf sendEmail-v1.56.tar.gz
[root@tang ~]# cp sendEmail-v1.56/sendEmail /usr/local/bin/
[root@tang ~]# chmod 755 /usr/local/bin/sendEmail
[root@tang ~]# vi /opt/sendEmail.sh
#!/bin/bash
#
# Filename: SendEmail.sh
# Revision: 1.0
# Date: 2019/05/29
# Author: Qicheng
# Email:
# Website: http://51xit.top/
# Description: tang邮件告警脚本
# Notes: 使用sendEmail
#
# 脚本的日志文件
LOGFILE=”/tmp/Email.log”
:>”$LOGFILE”
exec 1>”$LOGFILE”
exec 2>&1
SMTP_server=’smtp.qq.com’ # SMTP服务器,变量值需要自行修改
username=‘1581273154@qq.com‘ # 用户名,变量值需要自行修改
password=’cgdxhxqddtrafijh’ # 密码(QQ邮箱用的是授权码),变量值需要自行修改
from_email_address=‘1581273154@qq.com‘ #### 发件人Email地址,变量值需要自行修改
to_email_address=”$1” # 收件人Email地址,tang传入的第一个参数
message_subject_utf8=”$2” # 邮件标题,tang传入的第二个参数
message_body_utf8=”$3” # 邮件内容,tang传入的第三个参数
# 转换邮件标题为GB2312,解决邮件标题含有中文,收到邮件显示乱码的问题。
message_subject_gb2312=`iconv -t GB2312 -f UTF-8 << EOF
$message_subject_utf8
EOF`
[ $? -eq 0 ] && message_subject=”$message_subject_gb2312” || message_subject=”$message_subject_utf8”
# 转换邮件内容为GB2312,解决收到邮件内容乱码
message_body_gb2312=`iconv -t GB2312 -f UTF-8 << EOF
$message_body_utf8
EOF`
[ $? -eq 0 ] && message_body=”$message_body_gb2312” || message_body=”$message_body_utf8”
# 发送邮件
sendEmail=’/usr/local/bin/sendEmail’
set -x
$sendEmail -s “$SMTP_server” -xu “$username” -xp “$password” -f “$from_email_address” -t “$to_email_address” -u “$message_subject” -m “$message_body” -o message-content-type=text -o message-charset=gb2312
[root@tang ~]# chmod +x /opt/sendEmail.sh
[root@tang ~]# /opt/sendEmail.sh 1581273154@qq.com 测试 测试
监控磁盘情况,异常发邮件
1 | #!/bin/bash |
计划任务:
1 | [root@localhost tmp]# crontab -e |
添加
1 | 3个小时检查一次 |
十二、特殊符号
$0:当前脚本的文件名
$n:第n个位置参数
$*:传递给脚本或函数的所有参数,$*会将这些参数视为一个整体
$@:传递给脚本或函数的所有参数,$@会将所有参数当作同一字符串中的多个独立的单词
$#:脚本运行时携带的参数个数
$*:表示所有位置参数的内容
$?:最近一个命令的退出状态码
$$:当前shell的进程ID(PID)
$!:最近一个后台命令的PID
!!:执行上一条命令
IFS:内部字段分隔符,IFS环境变量定义了shell用作字段分隔符的一系列字符。默认情况下,shell会将下列字符当做字段分隔符:
- 空格
- 制表符
- 换行符
&>:将STDERR和STDOUT的输出重定向到同一个输出文件
&-:要关闭文件描述符,可以将它重定向到特殊符号&-
.:点操作符,点操作符是source命令的别名,它会在shell上下文中执行点操作符指定的脚本,而不是创建一个新的shell。
[ 操作符 文件或目录 ]
常用的测试操作符
-d:测试是否为目录(Directory)
-e:测试目录或文件是否存在(Exist)
-f:测试是否为文件(File)
-r:测试当前用户是否有权限读取(Read)
-w:测试当前用户是否有权限写入(Write)
-x:测试当前用户是否有权限执行(eXcute)