Shell 编程

Shell 编程

学习目标
  • 掌握脚本的编辑、运行和调试方法
  • 掌握 bash 变量的定义、引用、替换扩展
  • 掌握 bash 的各种流程控制语句
  • 掌握 bash 函数的定义和调用方法
  • 掌握命令行参数的使用方法
  • 掌握避免错误的处理方法使得脚本更健壮

任务1:编写一个最小化安装 CentOS7 后的配置脚本

要求:

  1. 配置 YUM 并更新系统
  • 安装 EPEL 仓库
  • 导入已安装所有仓库的 GPG 签名文件 /etc/pki/rpm-gpg/RPM-GPG-KEY-*
  • 配置所有仓库的国内镜像URL
  • 更新系统
  1. 安装常用的必要的软件
  • bash-completion vim-enhanced
  • net-tools psmisc yum-cron yum-utils yum-security
  • wget curl elinks w3m lftp mailx mutt rsync ntp git bind-utils
  • sysstat htop dstat nload nethogs iftop
  • rkhunter aide denyhost fail2ban lynis
  1. 系统基本配置
  • 设置语言、时区
  • 设置主机名
  • 创建一个普通用户并为其设置口令
  • 禁用 chrony 和 ntp 服务
  • 使用 ntpdate 配置每日时间同步
  • 限制每个用户能打开的最大文件数
  • 限制仅 wheel 组的人才能使用 sudo 和 su 命令
  • 关闭 SELINUX
  • 确保网络参数配置正确后关闭 NetworkManager 服务
  1. 安全配置
  • SSH
    • 允许使用口令登录但 root 用户只能使用密钥登录
    • 预先写入 root 远程 ssh 登录的公钥并为 .ssh 目录及其文件设置安全权限
    • 关闭 DNS 反向解析检查
    • ssh 回话闲置 5 分钟后自动注销登录
    • 安装配置 fail2ban
  • 口令
    • 口令必须大于12个字符
    • 必须至少包含四类字符中的一个
  • 网络
    • 开启防火墙 22,80,443 端口
  • 扩展:使 postinstall.sh 脚本同时适用于 CentOS 6/7
  • Vagrant 自动部署 – https://www.vagrantup.com/docs/provisioning/shell.html

任务2:根据 shell 类型统计用户数

要求:

  1. shell 类型通过命令行参数 -s <SHELL> 的形式指定
  • 显示 shell 类型为 SHELL 的用户数并列出所有用户名
  • SHELL 必须是 /etc/shells 文件中存在的类型,否则不执行脚本且退出状态码为1
  1. 当没有给出命令行参数或命令行参数为 -h | --help 时,显示帮助信息并退出
  2. 当命令行参数为其他值时,显示提示信息且退出状态码为2

若脚本名为 usersbysh,执行效果如下:

$ usersbysh -s bash
bash - 3 users , they are: root,student,tony
$ echo $?
0

$ usersbysh -s zash
Invalid shell.
$ echo $?
1

$ usersbysh
Usage: usersbysh -s <SHELL>|-h|--help
根据 shell 类型统计用户数并显示用户列表
$ echo $?
0

$ usersbysh --help
Usage: usersbysh -s <SHELL>|-h|--help
根据 shell 类型统计用户数并显示用户列表
$ echo $?
0

$ usersbysh -a
usersbysh:无效选项 -- a
Try 'usersbysh --help|-h' for more information.
$ echo $?
2

任务3:根据用户选择显示不同类型的系统信息

要求:

  1. 向用户展示功能菜单,具有如下功能:
  • 显示所有登录用户
  • 显示系统平均负载
  • 显示物理内存的使用
  • 显示交换空间的使用
  • 显示磁盘空间的使用
  • quit或q
  1. 读取用户从键盘上的选择
  • 根据不同的选择执行相应的命令
  • 选择 quit 或 q 时退出脚本
  • 输入错误的选择时退出脚本,且退出状态吗返回1

任务4:任务3扩展

要求:

  1. 当用户选择显示相应信息后不退出,而让用户继续选择,继续显示相应内容,用户选择 quit 菜单项才退出。
  2. 使用 while 语句 和 select 语句分别完成。

任务5:检测与服务器处于同一子网的每台主机的连通性

要求:

  1. 通过 ip|ifconfig 命令获取本机 IP 地址
  • 使用变量替换扩展删除最后一位十进制数
  1. 通过 ping 命令测试当前主机与 1…254 所有主机的连通性
  • 若能 ping 通就显示 “<IP> is up.”,其中的IP要换为真正的IP地址,且以绿色显示
  • 若不能 ping 通就显示"<IP> is down.",其中的IP要换为真正的IP地址,且以红色显示
  1. 请分别使用 whileuntilfor (两种形式)循环实现

提示 为避免突发性的线路故障发生,在使用 ping 时,每次等待 ping 的响应时间可以设置稍长一些(如:-W 2

任务6:任务5 的函数实现方式

要求:

  1. 使用函数实现获取本机 IP 地址
  2. 使用函数实现一台主机的连通性判定
  3. 在主程序中调用函数判定指定范围内的所有主机的在线情况

扩展 根据子网掩码的值确定要 ping 的主机的 IP 地址范围

任务7:找出使用率大于 90% 的所有分区

要求:

  1. 使用 read 配合 while 语句实现
  2. 将 df 命令的输出结果通过管道传递给 while
  • 不要对虚拟文件系统进行操作
  1. 将结果通过邮件发送给 root
  • 只有当有使用率大于 90% 的分区时才发送邮件
  • 邮件主题为:Warn: Disk Usage on <主机的FQDN>
  • 邮件体包含:找出的分区设备名(挂装点)和使用率
  1. 思考1:循环控制语句
  • 若 df 的输出结果以使用率降序排序后再通过管道传递给 while 时,在循环体内可以使用哪个循环控制语句配合条件判断来优化?
  • 若 df 的输出结果以使用率升序排序后再通过管道传递给 while 时,在循环体内可以使用哪个循环控制语句配合条件判断来优化?
  1. 思考2:下面的命令行能完全实现要求的功能吗?为什么?
  • df -PT |egrep -v '^Filesystem|tmpfs'|sort -nr -k6 | awk '{print $6 , $7}'|mail -s "Warn: Disk Usage on $(hostname -f)" root
  • df -PT |egrep -v '^Filesystem|tmpfs' | sed 's/%//' |awk '$6>90 {printf "%d%% used on %s (%s)\n", $6, $7, $1}'|mail -s "Warn: Disk Usage on $(hostname -f)" root

任务8:找出使用率大于 N% 的所有分区

任务7 扩展

**要求:**分别实现如下两种情况

  1. N 通过命令行参数传递给脚本
    • 若 N 不是值为 0~99 的数字,给出出错提示并退出脚本且退出状态码为1
  2. N 通过 read 从键盘读取
  • 使用循环由键盘读取 N,直至读取的值为 0~99 的数字为止

任务9:打印 1~N 之间的所有 奇数|偶数|素数

要求:

  1. 编写一个函数用于判断一个字符串是否为纯数字的值
  2. 编写三个函数分别用于打印 1~N 之间的所有 奇数|偶数|素数
  3. 编写一个函数显示脚本的格式和功能
  4. 脚本的命令行参数
  • 第一个参数必须为一个数字,否则显示使用帮助信息,并设置退出状态码为1
  • -o|–odd :打印奇数
  • -e|–even:打印偶数
  • -p|–prime:打印素数
  • -a|–all:打印奇数、偶数和素数
  • -h|–help:显示使用帮助信息
  • 对于其他的参数应该报错,并设置退出状态码为2
  • 可以同时指定多个参数,例如 -o -p 表示既打印奇数又打印素数

提示 命令行参数处理:getoptsgetopt

CH10 - bash 脚本编程

条件测试1 - test 或 []

[ ]                 test
[ 0 ]               test 0      
[ 1 ]               test 1
[ string ]          test string

[ $UID == 0 ]         比较    [ $UID==0 ]
[ $(id -u) == 0 ]     比较    [ $(id -u)==0 ] 

使用命令列表替换 if 语句

if [ ];   then echo T; fi 
if [ 0 ]; then echo T; fi 
if [ 1 ]; then echo T; fi 
if [ string ]; then echo T; fi 

[ ]   && echo T
[ 0 ] && echo T
[ 1 ] && echo T
[ string ] && echo T
if [ ];   then echo T; else echo F; fi 
if [ 0 ]; then echo T; else echo F; fi 
if [ 1 ]; then echo T; else echo F; fi 
if [ string ]; then echo T; else echo F; fi 

[ ]   && echo T || echo F
[ 0 ] && echo T || echo F
[ 1 ] && echo T || echo F
[ string ] && echo T || echo F

使用命令列表替换 if 语句 (续)

[ ]   && echo T
[ 0 ] && echo T
[ 1 ] && echo T
[ string ] && echo T

! [ ]   && echo F
! [ 0 ] && echo F
! [ 1 ] && echo F
! [ string ] && echo F

[ ]   && : || echo F
[ 0 ] && : || echo F
[ 1 ] && : || echo F
[ string ] && : || echo F
~]# help :
:: :
    Null command.

    No effect; the command does nothing.

    Exit Status:
    Always succeeds.
~]# help true false
true: true
    Return a successful result.

    Exit Status:
    Always succeeds.
false: false
    Return an unsuccessful result.

    Exit Status:
    Always fails.

条件测试2 - (())

(( ))                 比较    [ ]
(( 0 ))               比较    [ 0 ]
(( 1 ))               比较    [ 1 ]
(( string ))          比较    [ string ]

条件测试3 - 根据命令结果状态码测试

id -u > /dev/null
echo $?

grep -q '^osmond\>' /etc/passwd || useradd osmond
ping -c1 -W1 8.8.8.8 > /dev/null && elinks --dump http://whatismyip.org || ip r s

检查字符串变量空值与非空值

  • 检查空
    [ “ n a m e " = " " ] [ − z " name" = "" ] [ -z " name"=""][z"name” ]
    [ ! “ n a m e " ] [ " X name" ] [ "X name"]["X{name}” = “X” ]
  • 检查非空
    [ “ n a m e " ! = " " ] [ − n " name" != "" ] [ -n " name"!=""][n"name” ]
    [ “ n a m e " ] [ " X name" ] [ "X name"]["X{name}” != “X” ]

检查字符串变量是否为纯数字(正整数)的方法

((n>0)) 2> /dev/null 
[ $n -gt 0 ] 2> /dev/null 
[ -n "$n"  -a  -z "$(sed 's/[0-9]//g' <<< "$n")" ]
[ -n "$n"  -a  -z "$(egrep -o '[^0-9]+' <<< "$n")" ]
[ -n "$n"  -a  -z "${n//[0-9]/}" ]
[ -n "$n"  -a  "$n" == "${n//[0-9]/}" ]

[[ -n "$n"  &&  -z "$(sed 's/[0-9]//g' <<< "$n")" ]]
[[ -n "$n"  &&  -z "$(egrep -o '[^0-9]+' <<< "$n")" ]]
[[ -n "$n"  &&  -z "${n//[0-9]/}" ]]
[[ -n "$n"  &&  "$n" == "${n//[0-9]/}" ]]

在 if 、while、until 中使用条件测试

~]# help if while until
if: if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi
    Execute commands based on conditional.

    Exit Status:
    Returns the status of the last command executed.

while: while COMMANDS; do COMMANDS; done
    Execute commands as long as a test succeeds.

    Exit Status:
    Returns the status of the last command executed.

until: until COMMANDS; do COMMANDS; done
    Execute commands as long as a test does not succeed.

    Exit Status:
    Returns the status of the last command executed.

举例:从标准输入读取n的值,直至为全数字的整数为止

n=
until ((n>0)) 2>/dev/null
do
  read -p "Pls input a number: "  n
done
###
n=
while ! ((n>0)) 2>/dev/null
do
  read -p "Pls input a number: "  n
done
n=
while true
do
  read -p "Pls input a number: "  n
  ((n>0)) 2>/dev/null && break
done
###
n=
until false
do
  read -p "Pls input a number: "  n
  ((n>0)) 2>/dev/null && break
done

循环控制语句与 Shell 特性

|while
|until
|for

done|

done<
done<<<
done<<END

done>
done>>

函数

  • 定义
    • 是被命名的的 shell 脚本片段
  • 显示
    • declare -F
    • declare -f
    • declare -f funName
  • 执行
    • 函数与主程序在一个 shell 内执行
    • 主程序与函数中同名变量是一个变量
    • 在函数中使用 local 定义的变量只在此函数及其调用的函数中起作用
  • 返回值
    • 状态返回值 exit N
    • 结果返回值 可通过标准输出传递
a=100; b=200

mytest1 () {
  local a
  echo -e "($BASHPID) \t $[a=a+b] \t $a$b"
  mytest2
}
mytest2 () {
  echo -e "($BASHPID) \t $[++a]"
}
declare -f mytest{1,2}

echo $a $b
mytest1
echo $?
echo $a $b
echo $BASHPID
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨烦信息

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值