Shell自动化编程完全指南

shell自动化编程详解

一、shell自动化编程-基础与变量

1.环境准备

  • 修改vimrc文件,达到控制vim创建,编辑文件的动作 给文件添加注解
    • 当前用户家目录下 ~/.vimrc
    • 放在/etc/vimrc
sh 复制代码
set ignorecase 
set ignorecase
autocmd BufNewFile *.py,*.cc,*.sh,*.java exec ":call SetTitle()"
func SetTitle()
if expand("%:e") == 'sh'
call setline(1, "#!/bin/bash")
call setline(2,"########################################################")
call setline(3, "# File Name:".expand("%"))
call setline(4, "# Version:V1.0")
call setline(5, "# Author:liux")
call setline(6, "# Desc:")
call setline(7,"########################################################")
endif
endfunc

2.shell脚本执行方式

  • 通过sh或bash执行脚本 执行脚本最常用的方式
  • 通过.(点)或source
  • !符号含义 用于指定脚本默认的命令解释器

3.书写脚本每次用户登录后显示系统的基本信息

sh 复制代码
vim 02.sys_login_info.sh
#!/bin/bash
########################################################
# File Name:02.sys_login_info.sh
# Version:V1.0
# Author:liux
# Desc:用户登录后显示系统基本信息
########################################################

#1.赋值

sys_hostname=`hostname`
sys_ip_addrs=`hostname -I`
sys_mem_total=`free -h |awk 'NR==2{print $2}'`
sys_mem_free=`free -h |awk 'NR==2{print $NF}'`
sys_load=`uptime |awk '{print $(NF-2),$(NF-1),$NF}'`

#2.输出

cat <<EOF
     主机名:  ${sys_hostname}
     ip地址:  ${sys_ip_addrs}
     总内存:  ${sys_mem_total}
     可用内存:${sys_mem_free}
     系统负载:${sys_load}
EOF

#创建软链接到指定目录  用户登录会加载/etc/profile.d/目录下以.sh结尾的文件
ln -s /server/scripts/devops-shell/02.sys_login_info.sh /etc/profile.d/sys_login_info.sh

4.特殊变量

4.1特殊变量-位置变量
变量 含义 应用场景
$n 执行脚本后面的第n个参数 n表示数字 命令行与脚本内部桥梁
$0 获取脚本的名字 用于输出脚本的格式或帮助的时候。用于错误提示输出帮助
$# 统计脚本的参数个数 一般与判断结合,检查脚本参数个数
$@ 循环取出脚本所有参数 数组中或循环中
$* 取出脚本所有参数 数组中或循环中
4.2特殊变量-状态变量
变量 含义 应用场景
$? 上一个命令、脚本的返回值。0表示正确,非0即错误 一般用于判断检查命令结果
$$ 当前脚本的pid 一般写在脚本中获取脚本pid
$! 上一个脚本/命令的pid
4.3特殊变量-变量子串
  • ${#parameter} 统计字符长度(变量中有多少个字符)

二、shell自动化编程-运算符-条件表达式-if

1.运算方法

运算的命令/符号 说明 应用场景
awk 可以进行计算,带小数,可以与shell脚本进行变量传递
bc 带小数 一般计算都可以用bc.需要安装
expr 进行计算,整数 一般用于检查输入内容是否为数字
let 进行计算,整数,变量直接使用即可 用于计算i++
$(()) 进行计算,整数,变量直接使用即可
$[] 进行计算,整数,变量直接使用即可
1.1 awk进行计算
sh 复制代码
awk 'BEGIN{print 1/3}'
awk 'BEGIN{print 1/3*100}'

#在awk中使用shell变量
#awk -v选项用于创建或修改awk中的变量。 -v是shell脚本与awk桥梁
#在awk中各种变量直接使用即可,不要加上$n1,如果加上了会被awk识别为取列
awk -vn1=1  -vn2=3  'BEGIN{print n1/n2}'
1.2 bc用法
sh 复制代码
#-l显示小数
echo 1/3 |bc -l
echo 2^10 |bc -l
1.3 expr
  • 使用注意事项:使用空格,对*号转义
sh 复制代码
[root@m01 ~]# expr 1+1
1+1
[root@m01 ~]# expr 1 + 1
2
[root@m01 ~]# expr 2 * 2
expr: 语法错误
[root@m01 ~]# expr 2 \* 2
4
[root@m01 ~]# expr 2 / 2
1
  • 案例:对输入的内容检查是否为数字
sh 复制代码
[root@m01 devops-shell]# vim 03.check-num.sh

#!/bin/bash
########################################################
# File Name:03.check-num.sh
# Version:V1.0
# Author:liux
# Desc:
########################################################

#1.vars
#num=$1
read -p "请输入数字:" num

#2.检查是否为数字
expr $num \* 0 + 1 &>/dev/null && echo "$num是数字"
expr $num \* 0 + 1 &>/dev/null || echo "$num不是数字"
1.4 let <math xmlns="http://www.w3.org/1998/Math/MathML"> ( ( ) ) (()) </math>(())[]
sh 复制代码
n1=666
n2=999
let c=n1+n2 

n1=666
n2=999
echo $((n1+n2))

n1=666
n2=999
e=$[n1+n2]

案例:计算传入的两个参数的值

sh 复制代码
[root@m01 devops-shell]# vim 04.calc_num.sh
#!/bin/bash
########################################################
# File Name:04.calc_num.sh
# Version:V1.0
# Author:liux
# Desc:
########################################################

#1.vars
num1=$1
num2=$2

#2.检查是否位数字
expr $num1 \* 0 + $num2 \* 0 + 1 &>/dev/null || {
  echo "Usage: $0 数字1 数字2,2个数字"
  exit 1
}

#3.计算
awk -vn1=$num1 -vn2=$num2 'BEGIN{print n1+n2}'
awk -vn1=$num1 -vn2=$num2 'BEGIN{print n1-n2}'
awk -vn1=$num1 -vn2=$num2 'BEGIN{print n1*n2}'
awk -vn1=$num1 -vn2=$num2 'BEGIN{print n1/n2}'

2.条件表达式

  • 大部分情况下使用[ ]进行判断
  • 使用正则的时候用[[ ]]两对中括号
2.1条件表达式符号
符号 说明
-f file 判断是否为文件,是为真
-d dir 判断是否为目录,是为真
-x 判断是否有执行权限,是为真
-s 判断文件内容是否为空 ,非空为真
-r 是否有读权限
-w 是否有写权限
-nt 两个文件修改时间,是否更加新
-ot 两个文件修改时间,是否更加老
-L 软链接
-e 是否存在(任何类型文件)
sh 复制代码
#判断文件是否存在
[ -f /etc/hosts ] && echo "成立 " || echo "失败"


#检查目录是否存在
[ -d /etc/ ] && echo "成立 " || echo "失败"

#检查/etc/rc.d/rc.local是否有执行权限
[ -x /etc/rc.d/rc.local ] && echo "成立 " || echo "失败"
#ip命令是否有执行权限,如果没有则退出
[ -x /sbin/ip ] || exit 1

#-s检查文件内容是否为空,非空则为真
[ -s /etc/hosts ] && echo "成立 " || echo "失败"

注意:

​ && 表示前面命令执行成功然后执行echo 成立。

​ || 表示前面命令执行失败了,echo 失败

2.2对比字符串
字符串对比 说明
"str1" = "str2" str1等于str2,则为真
-z "str" zero 检查str字符串是否为空,空的则为真
sh 复制代码
[ "$UID" != "0" ] && exit 4 #判断是否为root执行

[ -z  "$str" ] && echo "成立 " || echo "失败"
2.3比大小(整数) [ ]
  • -eq equal 等于
  • ne not equal 不等于
  • -gt great than 大于
  • -ge great equal 大于等于
  • -lt less than 小于
  • -le less equal 小于等于
2.4逻辑判断
  • 多个条件同时成立,或者,取反
条件表达式 [ ]
与(同时成立) 条件1 -a 条件2 (and)
条件1 -r 条件2 (or)
!
2.5正则 [[ ]]
sh 复制代码
num=666
[[ $num =~ [0-9] ]] && echo 成立 || echo 失败

num=liux996
[[ $num =~ [0-9] ]] && echo 成立 || echo 失败
#前的正则,仅仅表示变量中只要有数字就行


#用正则精确匹配数字
[[ $num =~ ^[0-9]+$ ]] && echo 成立 ||echo 失败
#变量的值 开头结尾中间全是数字 连续数字
2.6 案例:比较两个数字的大小
sh 复制代码
[root@m01 devops-shell]# vim compare_num.sh

#!/bin/bash
########################################################
# File Name:compare_num.sh
# Version:V1.0
# Author:liux
# Desc:
########################################################
#1.vars
num1=$1
num2=$2

#2.检查是否为数字
[[ $num1 =~ ^-?[0-9]+$ ]] || {
  echo "$num1 不是数字"
  exit 1
}
[[ $num2 =~ ^-?[0-9]+$ ]] || {
  echo "$num2 不是数字"
  echo 1
}

#3.比较大小
[ $num1 -gt $num2 ] && {
  echo "$num1 大于 $num2"
}

[ $num1 -lt $num2 ] && {
  echo "$num1 小于 $num2"
}
[ $num1 -eq $num2 ] && {
  echo "$num1 等于 $num2"
}


#测试
[root@m01 devops-shell]# sh compare_num.sh 1 -1
1 大于 -1

3.if判断

  • 单分支判断
sh 复制代码
if 条件;then
   满足条件后执行的内容。
fi
  • 双分支判断
sh 复制代码
if 条件;then
   满足条件后执行的内容。
else
   不满足条件执行的内容。
fi
  • 多分枝判断
sh 复制代码
if 条件;then
   满足条件后执行的内容。
elif 条件;then   #else if 
   满足elif条件,执行的内容。
elif 条件;then
   满足elif条件,执行的内容。   
else
   不满足条件执行的内容。
fi

案例1:检查根分区磁盘空间使用率

sh 复制代码
[root@m01 devops-shell]# vim 05.disk_check.sh

#!/bin/bash
#1.vars
root_usage_now=`df -h |awk -F "[ %]+" '$NF=="/" {print $(NF-1)}'`
root_usage_warning=80

#2.进行比较
if [ $root_usage_now -gt $root_usage_warning ];then
   echo "磁盘空间不足,使用率是$root_usage_now%"
else
   echo "磁盘空间足,使用率是$root_usage_now%"
fi

案例2:输出指定用户的信息

  • 执行脚本输入用户名(参数/read)

  • 判断用户是否存在,如果不存在则提示用户不存在,退出脚本.

  • 如果用户存在输出用户的信息.

    • 是否可以登录(命令解释器)

    • uid,gid(过滤)

    • 用户家目录

    • 最近1次登录情况

    • 属于用户的文件(很多)

sh 复制代码
[root@m01 devops-shell]# vim 06.secure_check_user.sh

#!/bin/bash

#1.输入用户名
read -p "请输入用户名" user

#2.检查用户是否存在
#判断输入变量不能为空
if [ "${user}x" = "x" ];then
    echo "请输入用户名:"
    exit 1
fi

#是否存在
id $user &>/dev/null
if [ $? -ne 0 ];then
    echo "用户$user 不存在"
    exit 2
fi

#3.用户信息
user_shell=`awk -F: -vname=$user '$1==name{print $NF}' /etc/passwd`
if [ "$user_shell" = "/bin/bash" ];then
     if_login="可以登录"
else
     if_login="无法登录"
fi

#uid,gid,家目录
user_ids=`awk -F: -vname=$user '$1==name{print $3,$4}' /etc/passwd`
user_homedir=`awk -F: -vname=$user '$1==name{print $(NF-1)}' /etc/passwd`

#最近登录信息
user_login_info=`lastlog |awk -vname=$user '$1==name'`

#用户文件信息
user_files=`find / -type f -user liux 2>/dev/null`

#4.输出

cat <<EOF
     用户名:$user
     是否可以登录:$if_login
     用户uid,gid:$user_ids
     用户家目录:$user_homedir
     最近的登录情况:$user_login_info
     用户的文件:$user_files
EOF

4.case语句

  • 条件分支语句,一般用于实现有多种选择的脚本

  • case语句功能,可以通过if+elif+else形式进行替换

  • 格式

sh 复制代码
case "变量"  in  
   1)
         命令;;
   2)
         命令;;
   *)
         命令(保底的默认输出)
esac

5.shell编程-函数

  • 格式
sh 复制代码
#定义方式01 最完整
function lidao_show() {
内容
 return n  #函数的返回值
}
#定义方式02   精简写法 一般使用这一种.
lidao_show() {
命令
 return n  #函数的返回值
}
  • 函数传参
参数 shell脚本中 函数中
$n 脚本的第n个参数 函数的第n个参数
$0 脚本的名字 脚本的名字
$# 脚本的参数个数 函数的参数个数
<math xmlns="http://www.w3.org/1998/Math/MathML"> @ / @/ </math>@/* 脚本的所有参数 函数的所有参数

案例:书写sersync服务的管理脚本

  • sh data_sync.sh start|stop|restart|status

  • 需求

    • 如果用户输入的是start,则运行sersync启动的命令。

    • 如果用户输入的是stop,则运行关闭sersync的命令。

    • 如果用户输入的是status,则显示sersync是否运行中,pid。

    • 如果用户输入的是restart,则运行stop的命令,然后运行start的命令。

    • 如果用户输入的是其他的内容,则提示输入错误,提示格式。

sh 复制代码
[root@m01 devops-shell]# vim  07.data_sync.sh 

#!/bin/bash
########################################################
# File Name:07.data_sync.sh
# Version:V1.0
# Author:liux
# Desc:sersync服务管理脚本
########################################################

#1.vars命令行传参
choice=$1
sersync_dir=/app/tools/sersync
srv_status=`ps -ef|grep sersync |egrep -v "grep|$0" |wc -l`
#2.书写功能对应的函数
start() {
  if [ $srv_status -eq 0 ];then
    ${sersync_dir}/bin/sersync -rdo ${sersync_dir}/conf/confxml.xml &>/dev/null
  else
    echo "sersync is already running"
  fi
}
stop() {
  if [ $srv_status -eq 0 ];then
    echo "sersync is already stopped"
  else
    pkill sersync
  fi
}
restart() {
  pkill sersync
  ${sersync_dir}/bin/sersync -rdo ${sersync_dir}/conf/confxml.xml &>/dev/null
}
status() {
  if [ $srv_status -eq 0 ];then
    echo "sersync is not running"
  else
    pid=`ps -ef|grep sersync |egrep -v "grep|$0" |awk '{print $2}'`
    echo "sersync is running,pid is $pid"
  fi

}
#3.case语句
main() {
  case "$choice" in
    start)
          start
          ;;
    stop)
          stop
          ;;
    restart)
          restart
          ;;
    status)
          status
          ;;
    *)
          echo "Usage:$0 {start|stop|restart|status}"
  esac
}

main


#测试
sh 07.data_sync.sh status

6.必知必会命令

6.1 检查端口
sh 复制代码
检查端口是否存在
ss -lntup |grep 80
netstat -lntup |grep 80
lsof -i:80

#检查ip、端口能否访问
telnet 
nc 
#-z 无io模式,用于检查端口是否连通。
nc -z 10.0.0.61 80

nmap
6.2 检查进程
sh 复制代码
top ps
6.3 检查网络
sh 复制代码
ping -c2 www.baidu.com
iftop
6.4 检查web 域名或ip能否访问
sh 复制代码
curl/wget
#-v显示过程   -I获取响应头
#-s slient 安静模式 如果不使用默认输出下载进度
#-o curl的输出到指定位置的文件
#-w 按照指定格式与内容输出 %{http_code}状态码   更多格式 man 
curl 搜索 variable
curl: -v -L -H -I -w -o

#-t 失败后,重复尝试次数、-T timeout 超时时间 -q 不显示wget输出  --spider 不下载文件,仅访问
wget -t 3 -T 1 -q --spider www.baidu.com

案例:检查指定地址和端口是否可以访问

  • sh check_access.sh 10.0.0.61 22
  • nc
sh 复制代码
[root@m01 devops-shell]# cat 08.check_access.sh 
#!/bin/bash
########################################################
# File Name:08.check_access.sh
# Version:V1.0
# Author:liux
# Desc:检查指定ip和端口是否可以访问
########################################################

#加载系统内置函数库
. /etc/init.d/functions

addr=$1
port=$2

#检查nc命令是否存在
check_nc() {
  which nc &>/dev/null || {
    yum -y install nc
  }
}
#检查域名和ip是否合法
check_valid() {
  [[ "$addr" =~ ^([0-9.]+|[a-z.]+)$ ]] || {
     echo "输入的域名不合法,$addr"
     exit 1
  }
  [[ "$port" =~ ^[0-9]+$ ]] || {
     echo "输入的ip不合法,$port"
     exit 1
  }

}  
#用nc命令检查
check_port() {
  nc -z $addr $port
  if [ $? -eq 0 ];then
      action "可以访问,是通的" /bin/true
  else
      action "无法访问" /bin/false
  fi
}
#调用函数
main() {
  check_nc
  check_valid
  check_port
}
main


 "=~" 用于判断左边字符串和右边的正则表达式是否匹配
 正则表达式^表示起始,$表示结束,?表示0个或者1个

三、shell自动化编程-循环

1.for循环

  • 最常用的for循环方式 大部分场景可用
sh 复制代码
for 变量  in 候补清单(列表)
do
   命令
done

#for循环一般搭配着{1..10}  `seq 10`  `ls /etc/`
  • c语言格式for循环 对数组循环使用
sh 复制代码
for((i=1;i<=10;i++))
do
    echo $i
done

案例:使用for循环在/liux目录下通过随机小写10个字母加固定字符串liux批量创建10个html文件

  • 例如:apquvdpqbk_liux.html

  • 生成随机数

sh 复制代码
#1.
uuidgen
#2. -l密码长度,-d数字数量,-s special 特殊字符 -C 大写字母 -c小写字母
mkpasswd -l 10 -d 0 -s 0 -C 0

#3.-c取反,-d删除 /dev/urandom 字符设备,生成随机字符
tr -cd 'a-z' </dev/urandom |head -c 10
#4.%N纳秒
date +%N |md5sum
#5
echo $RANDOM
  • 代码
sh 复制代码
[root@m01 devops-shell]# vim 09.touch_files.sh

#!/bin/bash

#1.vars
dir=/liux

#2.检查
[ ! -d $dir ] && mkdir -p $dir

#3.for循环

for n in {1..10}
do
  file_name_pre=`tr -cd 'a-z' </dev/urandom |head -c 10`
  file_name_full=${file_name_pre}_liux.html
  touch ${dir}/${file_name_full}
done

2.while循环

  • 加入条件
  • 死循环
  • 读取文件
2.1while循环通用格式
sh 复制代码
while 条件
do
     命令
done

#温馨提示: while循环只会在满足条件后运行
2.2案例:生成随机数,判断数字是什么
  • 如果输入的数字比随机数大,提示大了,

  • 如果输入数字比随机数小,提示小了,

  • 如果等于提示恭喜

  • 额外要求:

    • 用了1-3次 超越了99.99%人

    • 用了4-6次 超越80%的人

    • 其他 超越了70%的人

sh 复制代码
[root@m01 devops-shell]# vim 10.guess_num.sh

#!/bin/bash
#1.vars
num_rand=$(( RANDOM%100 ))
i=1

#2.检查输入的是否为数字
num_check() {
  [[ $num_rand =~ ^[0-9]+$ ]] || {
    echo "请输入数字"
    continue
  }
}

#3.排名功能
ranking() {
  cnt=$((i-1))
  if [ $cnt -le 3 ];then
     echo "恭喜您,使用了$cnt次,超越了99.99%的用户"
  elif [ $cnt -ge 4 -a $cnt -le 6 ];then
     echo "恭喜您,使用了$cnt次,超越了80%的用户"
  else
     echo "恭喜您,使用了$cnt次,超越了70%的用户"
  fi
}

#4.用户输入数字,判断
input_num_compare() {
  read -p "请输入数字" num
  num_check
  let i++
  if [ $num -gt $num_rand ];then
    echo "很抱歉,猜大了"
  elif [ $num -lt $num_rand ];then
    echo "很抱歉,猜小了"
  else
    echo "恭喜猜对了"
    ranking
    exit
  fi
}

#5.书写主函数
main() {
while true
do
  input_num_compare
done
}
main
  • while循环-读取文件内容
    • 需要在脚本中读取文件内容,多行。此时可以选择3剑客或while循环
sh 复制代码
#方式1:采用exec读取文件后,然后进入while循环处理。
exec<FILE
while read line
do
   cmd
    echo $line
done

#方式2:使用cat读取文件内容,然后通过管道进入 不适用于有变量传递场景使用。
cat FILE|while read line
do
   cmd
   echo $line
done

#方式3:在while循环结尾done通过输入重定向指定读取的文件。 推荐使用
while read line
do
   cmd
done<FILE

3.do-until循环

  • 无论条件是否满足,都会执行一次
sh 复制代码
#直到型循环: 一直循环,直到条件不满足.
until 条件
do
   命令
   命令
   ....
done
until 话费是否充足
do
   发短信
done

#先执行,再判断条件是否满足

4.循环控制语句

语句 含义 应用场景
exit 终止执行脚本,退出返回值 脚本结束加上exit
return 放在函数中,终止执行函数,函数返回值 写在函数中,检查函数命令运行是否成功
break 终止循环(退出) 需要在循环中退出循环
continue 终止(跳过)本次循环,进入下一次循环 要在循环中跳过某一次循环

四、shell编程-辅助功能

  • 颜色
sh 复制代码
for n in {30..48};do echo -e "\E[1;${n}mliux\E[0m" ; done
sh 复制代码
#创建环境变量或写入脚本开头
export RED="\E[5;31m"
export GREEN="\E[1;32m"
export BLUE="\E[1;34m"
export END="\E[0m"
#永久使用/etc/profile中即可

#写为函数
redecho() {
  echo -ne "\e[5;31m"
  echo -n "$@"
  echo -e "\e[0m"
  #echo -e "\e[5;31m $@ \e[0m"
}
greenecho() {
  echo -ne "\e[1;32m"
  echo -n "$@"
  echo -e "\e[0m"
}
blueecho() {
  echo -ne "\e[1;34m"
  echo -n "$@"
  echo -e "\e[0m"
}

五、shell编程-数组

sh 复制代码
names=(web01 web02 db01 nfs01 backup)
for n  in  ${names[@]}
do
     ping -c 1 -W 1  $n  
done
  • shell数组赋值
赋值
直接赋值 array=(ip01 ip02 ip03 ip04 ) array=( cat ip.txt) #cat ip.txt 三剑客命令获取指定内容
read命令赋值 read -a -p "输入数组中内容:" array 可以创建数组,空格分割即可
  • 案例:从键盘输入10个整数,求和、平均数。
sh 复制代码
[root@m01 devops-shell]# vim 11.read_sum.sh

#!/bin/bash
#0.vars
. ./func.sh
sum=0
avg=0
i=1
tmp_file=/tmp/num.txt

#1.初始化,判断文件是否存在
init() {
  [ -f $tmp_file ] && rm -f $tmp_file
  return $?
}

#2.读取用户输入
read_num() {
  while true
do
  [ $i -gt 10 ] && break
  read -p "请输入第${i}个数字:" num
  [[ "$num" =~ ^[0-9]+$ ]] || continue
  echo $num >>$tmp_file
  let i++
done
return $?
}

#3.进行计算
num_calc() {
  num_array=(`cat $tmp_file`)
  for num in ${num_array[@]}
  do
    let sum+=num
  done
  return $?
}

#4.输出总和与平均数
num_print() {
  avg=`echo "scale=2;$sum/10"|bc -l`
  rc=$?
  blueecho "用户输入的数字有:${num_array[@]}"
  greenecho "10个数字的总和是$sum,平均数是$avg"
  return $rc
}

#5.主函数
main() {
  init
  read_num
  num_calc
  num_print
}

main

六、shell编程-debug全流程

1.书写习惯

  • 注释
  • 变量:在脚本中尽量使用变量,变量命令规范,给变量加上注释
  • 函数:代码中尽可能使用函数,增加说明
  • 返回值:尽可能增加函数return功能,方便后期调试
  • 参数与选项检查:尽可能增加exit 返回值的功能,方便后期调试
  • 输出:书写代码的时候,可以多写一些echo用于在某些步骤中进行输出
  • 缩进:代码注意缩进

2.调试方法

  • -x 大部分情况使用 显示详细的执行过程
  • 精确显示执行过程
    • set -x 开启显示执行过程
    • set +x 关闭显示箱子过程
  • 注释法
  • 输出关键变量

七、再战三剑客

1.sed与变量

sh 复制代码
cat ip.txt
10.0.0.5
10.0.0.6
10.0.0.7
src=10.0.0
dst=172.16.1
sed "s#$src#$dst#g" ip.txt
172.16.1.5
172.16.1.6
172.16.1.7

2.awk判断

sh 复制代码
 awk '{if(NR<=5){print $0}}' /etc/passwd
 awk '{
       if(NR<=5){
          print $0
        }
 }' /etc/passwd
 

3.awk循环

sh 复制代码
awk 'BEGIN{for(i=1;i<=100;i++) {sum=sum+i} print sum }'

4.awk数组

  • awk数组专用于统计与分析
    • 去重统计次数
    • 去重求和
  • awk数组与shell数组区别
    • awk数组:关联数组,下标啥都行
    • shell数组:普通数组,下标数字,shell中也有关联数组
sh 复制代码
awk 'BEGIN{array[0]="lidao996";array["lidao"]=996;array[110]="sos"; print array[0]}'
  • awk专用于数组的循环
sh 复制代码
for(n  in 数组名字)
   print n(数组下标),数组名字[n]
         数组下标    对应的值
sh 复制代码
vim awk-array.txt
url               次数
img.oldboylinux.cn 6
bbs.oldboylinux.cn 7
avi.oldboylinux.cn 99
mp4.oldboylinux.cn 88

#以url为下标,次数为元素值的数组,输出内容
awk 'NR>1{array[$1]=$2}END{ for( url in array) print url,array[url] }' awk-array.txt
  • 去重统计次数
sh 复制代码
 vim url.txt
http://www.etiantian.org/index.html
http://www.etiantian.org/1.html
http://post.etiantian.org/index.html
http://mp3.etiantian.org/index.html
http://www.etiantian.org/3.html
http://post.etiantian.org/2.html


awk -F'/+' '{url[$2]=url[$2]+1}END{for(name in url) print name,url[name]}' url.txt

八、shell编程-安全基线检查脚本

1.身份鉴权

1.1配置口令复杂度
sh 复制代码
#默认格式
[root@m01 ~]# grep pwquality /etc/pam.d/system-auth
password    requisite     pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=

#修改
vim /etc/pam.d/system-auth
password requisite pam_pwquality.so try_first_pass local_users_only retry=3 difok=3 minlen=8 ucredit=-1 lcredit=-1 dcredit=-1 authtok_type=
#root用户也使用
enforce_for_root

#检查有没有配置密码复杂度
egrep -o 'minlen|[uld]credit' /etc/pam.d/system-auth |wc -l

minlen=8 至少8位

ucredit=-1 upper 大写字母 -号表示至少-1位 3 最多3位

lcredit=-1 lower 小写字母 -号表示至少-1位

dcredit=-1 digit 数字 -号表示至少-1

difok=3 与上个密码重复的内容

enforce_for_root 也限制root修改密码

1.2设置口令认证失败锁定次数
sh 复制代码
vim /etc/pam.d/system-auth
#auth是属于认证部分
auth required pam_tally2.so onerr=fail deny=3 unlock_time=20

#注:解锁用户 pam_tally2 -u liux --reset
#温馨提示: 这个规则放在最上面.
#如果需要root 也错误并锁定则增加even_deny_root即可

#检查是否配置
grep pam_tally2.so /etc/pam.d/system-auth |egrep -o 'deny=|unlock_time=' |wc -l
1.3配置口令生存周期
  • 是否配置新用户的密码过期
  • 检查是否最近30天有用户密码要过期
sh 复制代码
#新用户
#建议在/etc/login.defs文件中配置:

PASS_MAX_DAYS 90 #新建用户的密码最长使用天数
PASS_MIN_DAYS 0 #新建用户的密码最短使用天数
PASS_WARN_AGE 7 #新建用户的密码到期提前提醒天数

#命令修改已经存在的用户过期时间  -x最长使用天数  -w 过期提醒7天前
passwd -x 90 -n 0 -w 7 liux

#检查
chage -l liux

awk '/^PASS_MAX_DAYS/{print $2}' /etc/login.defs 或者
grep "^PASS_MAX_DAYS" /etc/login.defs |awk '{print $NF}'

awk '/^PASS_WARN_AGE/{print $2}' /etc/login.defs

#密码过期判断
#1. 可以登录系统 /bin/bash
awk -F: '$NF=="/bin/bash" {print $1}' /etc/passwd
#2. 获取用户密码过期时间chage -l 用户名看
chage -l root
#3. 过期时间与当前时间相减 (秒)
#4. 转换为天 与30天对比
#5. 与30对比即可
1.4配置使用ssh方式远程访问
sh 复制代码
#使用ssh远程连接,禁用telnet
#检查点检查服务是否运行或开启即可.
telnet #方式关闭
ssh #方式开启
[root@lb01 ~]# systemctl is-active sshd telnet
active
unknown
1.5配置历史口令使用策略
sh 复制代码
#最近5次的密码不能相同
password requisite pam_pwhistory.so remember=5

2.访问控制

2.1锁定无关用户
sh 复制代码
#不用的用户,锁定,注释

#lock (root)
passwd -l
#解锁 (root)
passwd -u
#通过
passwd -S 用户名查看是否锁定
/etc/shadow 第2列是否!!叹号
#可以注释掉用户.
lp nuucp hpdb sync adm ftp games
2.2清楚uid为0的非root用户
sh 复制代码
#检查是否存在 是否有uid是0 用户名不是root的用户.
awk -F: '$3==0 && $1!="root" {print $1}' /etc/passwd 
2.3控制为授权的suid/sgid
  • 取PATH变量存放的目录中的命令
sh 复制代码
#-perm 按照文件权限查找
find ${PATH//:/} /tmp/ -perm /+s 2>/dev/null |xargs ls -ld --color
2.4控制任何人都有写权限的目录
  • o上有w的目录
sh 复制代码
find / -type d -perm /o+w |xargs ls -ld
2.5控制任何人都有写权限的文件
  • o上有w的文件
sh 复制代码
find / -type f -perm /o+w 2>/dev/null |egrep -v "^/sys|^/proc" |wc -l
2.6删除没有属主的文件
sh 复制代码
find / -nouser -o -nogroup 2>/dev/null |wc -l
2.7控制异常或隐藏文件
sh 复制代码
#查找以点开始的文件
find / -name ".*" |xargs ls -ld

3.安全审计

3.1配置使用ntp
sh 复制代码
ntpdate ntp1.aliyun.com

4.入侵防护

4.1配置登录超时自动退出
sh 复制代码
export TMOUT=3
epxort HISTSIZE=10
export HISTFILESIZE=10
4.2限制root账户远程登录
sh 复制代码
PermitRootLogin no

九、脚本汇总

1.系统巡检脚本

sh 复制代码
[root@m01 devops-shell]# vim sys_info.sh
#!/bin/bash
########################################################
# File Name:sys_info.sh
# Version:V1.0
# Author:liux
# Desc:系统巡检基本脚本
########################################################

. /server/scripts/devops-shell/func.sh

#1.基础信息
base_info() {
  hostname=`hostname`
  ip=`hostname -I`
  blueecho  "#################################"
  blueecho  "###########基本信息##############"
  greenecho "主机名:$hostname"
  greenecho "ip地址:$ip"
  blueecho  "#################################"
} 

#2.负载
load_info() {
  load1=`uptime |awk -F'[ ,]+' '{print $(NF-2)}'`
  load5=`uptime |awk -F'[ ,]+' '{print $(NF-1)}'`
  load15=`uptime |awk -F'[ ,]+' '{print $NF}'`
  blueecho  "#################################"
  blueecho  "###########负载信息##############"
  greenecho "最近1分钟负载:$load1"
  greenecho "最近5分钟负载:$load5"
  greenecho "最近15分钟负载:$load15"
  blueecho  "#################################"
}

#3.内存
mem_info() {
  mem_total=`free -h |awk 'NR==2{print $2}'`
  mem_used=`free -h  |awk 'NR==2{print ($3+$6)}'`
  mem_used_percent=`free |awk 'NR==2{print ($3+$6)/$2*100"%"}'`
  blueecho  "#################################"
  blueecho  "###########内存信息##############"
  greenecho "总计内存:$mem_total"
  greenecho "已用内存:${mem_used}M"
  greenecho "内存使用率:$mem_used_percent"
  blueecho  "#################################"
}

#4.swap
swap_info() {
  swap_total=`free -h |awk 'NR==3{print $2}'`
  swap_used=`free -h  |awk 'NR==3{print ($3+$6)}'`
  swap_used_percent=`free |awk 'NR==3{print ($3+$6)/$2*100"%"}'`
  blueecho  "#################################"
  blueecho  "###########swap信息##############"
  greenecho "总计swap:$swap_total"
  greenecho "已用swap:${swap_used}M"
  greenecho "swap使用率:$swap_used_percent"
  blueecho  "#################################"
}

#5.磁盘

disk_info() {
  disk_total=`fdisk -l |grep '/dev/[sv]d[a-z][::]' |wc -l`
  root_size=`df -h | awk '$NF=="/"{print $2}'`
  root_used_percent=`df -h | awk '$NF=="/"{print $(NF-1)}'`
  blueecho  "#################################"
  blueecho  "###########磁盘信息##############"
  greenecho "总计硬盘个数:$disk_total"
  greenecho "根分区大小:${root_size}"
  greenecho "根分区使用率:$root_used_percent"
  blueecho  "#################################"
}

#6.进程
proc_info() {
  proc_total=`top -bn1 |awk 'NR==2{print $2}'`
  proc_running=`top -bn1 |awk 'NR==2{print $4}'`
  proc_stopped=`top -bn1 |awk 'NR==2{print $8}'`
  proc_zombie=`top -bn1 |awk 'NR==2{print $10}'`
  blueecho  "#################################"
  blueecho  "###########进程信息##############"
  greenecho "进程总数:$proc_total"
  greenecho "正在运行的进程数:${proc_running}"
  greenecho "挂起的进程数:$proc_stopped"
  greenecho "僵尸进程数:$proc_zombie"
  blueecho  "#################################"
}

main() {
  base_info
  load_info
  mem_info
  swap_info 
  disk_info
  proc_info
}

main

2.安全基线检查脚本

sh 复制代码
[root@m01 devops-shell]# vim   project_linux_base_line_check.sh 
#!/bin/bash
########################################################
# File Name:project_linux_base_line_check.sh
# Version:V1.0
# Author:liux
# Desc:安全基线检查脚本
########################################################

#1.加载函数
. /server/scripts/devops-shell/func.sh
printline() {
   blueecho "------------$1--------------"
}

# -ne不等于0
check_root() {
  [ $UID -ne 0 ] && {
    echo "只能root用户使用该脚本"
    exit 1
  }
}
check_cmd() {
  which wget nc ntpdate &>/dev/null
  [ $? -ne 0 ] && {
    echo "没有安装常用工具"
    exit 2
  }
}
#2.函数
#####身份鉴权#####
user_password_complex() {
  printline 1.检查密码复杂度
  pass_complex=`egrep -o 'minlen|[uld]credit' /etc/pam.d/system-auth |wc -l`
  if [ $pass_complex -eq 4 ];then
    greenecho "密码复杂度配置OK"
    rc=0
  else
    redecho "未配置密码复杂度failed"
    rc=1  
  fi
  return $rc
}

user_password_lock() {
  printline 2.检查密码认证失败锁定的配置
  pass_lock=`grep pam_tally2.so /etc/pam.d/system-auth |egrep -o 'deny=|unlock_time=' |wc -l`
  if [ $pass_lock -eq 2 ];then
    greenecho "密码认证失败锁定配置OK"
    rc=0
  else
    redecho "密码认证失败锁定配置failed"
    rc=1
  fi
  return $rc

}

user_password_expire() {
  printline 3.检查口令生存期配置
  pass_max_days=`awk '/^PASS_MAX_DAYS/{print $2}' /etc/login.defs`
  pass_warn_age=`awk '/^PASS_WARN_AGE/{print $2}' /etc/login.defs`
  max_days=90
  warn_age=7
  if [ $pass_max_days -le $max_days -a $pass_warn_age -ge $warn_age ];then
    greenecho "口令生存周期配置OK"
    rc=0
  else
    redecho "口令生存周期未配置failed"
    rc=1  
  fi
  return $rc 
}

check_user_password_expire() {
  expire_days=30
  printline 4.检查是否有口令最近${expire_days}天将要过期的用户
  login_user=`awk -F: '$NF=="/bin/bash" {print $1}' /etc/passwd`
  for name in $login_user
  do
    expire_status=`chage -l $name |awk -F:  'NR==2{print $2}'`
    [ "$expire_status" = " never" ] && continue
    time_expire_second=`date -d "$expire_status" +%s`
    time_now_second=`date +%s`
    time_leave_days=`echo "(${time_expire_second}-${time_now_second})/86400" |bc`
    if [ $time_leave_days -le $expire_days ];then
      redecho "用户${name}即将过期,你还有${time_leave_days}天"
    fi
  done
}

user_remote_access() {
  printline 5.检查是否开启未加密传输服务
  if [ nc -z localhost 23 &>/dev/null ];then
    redecho "开启了未加密的telnet服务"
    rc=1
  else
    greenecho "telnet服务已关闭"
    rc=0
  fi
}

user_password_history() {
  printline 6.检查是否配置历史口令重复要求
  pass_history=`grep pam_pwhistory.so /etc/pam.d/system-auth |grep -o remember |wc -l`
  if [ $pass_history -eq 1 ];then
    greenecho "历史口令重复策略配置OK"
    rc=0
  else
    redecho "未配置历史口令策略failed"
    rc=1
  fi
  return $rc
}

########访问控制########
deny_useless_user() {
  printline 7.检查是否禁用无用用户
  useless_user_cnt=`grep -v '^#' /etc/passwd |egrep 'lp|nuucp|hpdb|sync|adm|ftp|games' |wc -l`
  if [ $useless_user_cnt -eq 0 ];then
    greenecho "禁用了无用用户OK"
    rc=0
  else
    redecho "未禁用无用用户failed"
    rc=1
  fi
  return $rc
}

fake_root_user() {
  printline 8.检查是否存在uid为0,用户名不是root的用户
  fake_root=`awk -F: '$3==0 && $1!="root" {print $1}' /etc/passwd |wc -l`
  if [ $fake_root -eq 0 ];then
    greenecho "不存在这样的用户OK"
    rc=0
  else
    redecho "存在这样的用户failed"
    rc=1
  fi
  return $rc
}

check_uid_gid_file() {
  printline 9.检查是否存在额外的uid,gid文件
  uid_gid_file=`find ${PATH//:/ } /tmp/ -perm /+s 2>/dev/null|wc -l`
  if [ $uid_gid_file -eq 23 ];then
    greenecho "uid,gid文件OK"
    rc=0
  else
    redecho "uid,gid文件failed"
    rc=1
  fi
  return $rc

}

check_everybody_write_dir() {
  printline 10.检查是否有过大权限的目录
  dir_777=`find / -type d -perm /o+w |egrep -v '/tmp|/dev' |wc -l`
  if [ $dir_777 -eq 0 ];then
    greenecho "系统没有过大权限的目录OK"
    rc=0
  else
    redecho "系统有大过大权限的目录failed"
    rc=1
  fi
  return $rc

}

check_hidden() {
  printline 11.检查是否有隐藏文件或目录
  dir_list=`awk '$3~/xfs|ext[2-4]/{print $2}' /etc/fstab `
  for dir in $dir_list
  do
    hidden_cnt=`find $dir -xdev -name ".*" |egrep -v "/sys/|/proc/" |wc -l`
    greenecho "$dir 磁盘分区  隐藏文件的数量:$hidden_cnt"
  done
}

#####安全审计#####
check_ntp() {
  printline 12.检查是否配置了时间同步
  ntp_sync=`crontab -l|grep ntpdate |wc -l`
  if [ $ntp_sync -eq 1 ];then 
     greenecho "配置了时间同步OK"
    rc=0
  else
    redecho "没有配置时间同步failed"
    rc=1
  fi
  return $rc
}

#####入侵防护#####
check_env() {
  printline 13.检查是否配置了安全的环境变量
  
  if [ "$TMOUT"x != ""x -a $HISTSIZE -le 10 ];then
     greenecho "配置了安全的环境变量OK"
    rc=0
  else
    redecho "没有配置安全的环境变量failed"
    rc=1
  fi
  return $rc

}

check_root_remote_login() {
  printline 14.检查root是否可以远程登录
  root_remote_login=`awk '/^PermitRootLogin/{print $2}' /etc/ssh/sshd_config`
  if [ "$root_remote_login"x = "no"x ];then
    greenecho "root禁用了远程登录功能OK"
    rc=0
  else
    redecho "root开启了远程登录功能failed"
    rc=1
  fi
  return $rc

}
#####身份鉴权#####
user() {
  user_password_complex
  user_password_lock
  user_password_expire
  check_user_password_expire
  user_remote_access
  user_password_history
}

#####访问控制#####
access() {
  deny_useless_user
  fake_root_user
  check_uid_gid_file
  check_everybody_write_dir
  check_hidden
}

#####安全审计#####
audit() {
 check_ntp 
}

#####入侵防护#####
ruqin() {
  check_env
  check_root_remote_login
}

main() {
  check_root
  check_cmd
  user
  access
  audit
  ruqin
}

main
相关推荐
lihouyi10 小时前
Shell入门指南
自动化运维·全栈
liux35281 天前
Ansible集群批量管理与维护完全指南
自动化运维
小星运维日记3 天前
2026年五大自动化运维系统测评:企业运维如何突破效率瓶颈?
自动化运维·自动化运维平台·自动化运维系统·自动化运维中心·自动化运维产品
用户0591562441255 天前
# Go语言 Windows 桌面自动化实战:从零开始掌握 winput
自动化运维
小胖体育生5 天前
Ansible Playbook编写全教程:从入门到实战(附完整案例+最佳实践)
自动化运维
极客小云8 天前
【[Python自动化] 我写了一个工具,一键将几百个Word/PDF简历自动汇总到Excel,早早下班!】
自动化运维
draking12 天前
从 3 小时到 15 分钟:我们的发布效率提升 10 倍之路
自动化运维
智能运维指南14 天前
信创深化期ITSM选型:打破流程割裂,锁定全栈适配的智能方案
自动化运维·aiops·it管理·itsm·itsm厂商