shell脚本

什么是 shell

shell是命令解释器。用户输入命令,解释器负责翻译给内核,内核驱动硬件,返回shell
linuk 默认的解释器是bash 也叫 sh
[root@test3 ~]# ll /bin/sh
lrwxrwxrwx. 1 root root 4 Dec 27  2023 /bin/sh -> bash

什么是shell脚本
将多个可执行的命令写入到文件中,称之为shell脚本,只是脚本中还包含了,变量,表达式,循环判断语句等
shell是解释型语言,每次执行都需要解释器的介入


交互式: 我们输入一个命令他解释一下
非交互式: 写成脚本

shell的规范

1.shell开头写解释器 #!/bin/bash  #!/bin/sh
2.shell脚本以.sh结尾
3.shell注释信息
4.脚本尽量放在同一个目录

第一个shell脚本

输出hello world
[root@test3 ~]# cat 1.sh 
#!/bin/bash
echo 'hello world'

执行脚本的三种方式
第一种:bash sh
[root@test3 ~]# bash 1.sh 
hello world
[root@test3 ~]#  sh 1.sh 
hello world
第二种使用路径的方式取执行
相对路径执行
[root@test3 ~]# ./1.sh
-bash: ./1.sh: Permission denied
[root@test3 ~]# chmod +x 1.sh 
[root@test3 ~]# ./1.sh
hello world

绝对路径执行
[root@test3 ~]# /root/1.sh
hello world

第三种使用 source 或者 . 来执行
[root@test3 ~]# source 1.sh 
hello world
[root@test3 ~]# . 1.sh 
hello world

bash xx.sh
他会开启一个子shell去执行,执行脚本中的命令,执行完成后,子shell退出,他会把结果返回父shell中
source. 都是在父shell中执行,不会产生子shell 
[root@test3 ~]# cat 1.sh 
#!/bin/bash
name=111
[root@test3 ~]# bash 1.sh 
[root@test3 ~]# 
[root@test3 ~]# echo $name

[root@test3 ~]# source 1.sh
[root@test3 ~]# echo $name
111
[root@test3 ~]# unset name

shell变量

什么是变量
用一个固定的值,表示一串不固定的值
name=xx
dir=/code

变量种类
系统变量:env  系统自己定义好的,也成为全局环境变量
普通变量:自己定义的,经常是在文本中 ,也成为局部环境变量

安装生存周期分类
永久变量:一直都有,每次退出连接他都一直存在,写入/etc/profile
临时变量:退出系统就没了  ,直接在当前命令行定义就可以了

不加 export:只针对当前shell生效
加 export:针对当前登录的父shell以及子shell全部生效

定义环境变量
变量名称的定义方式
  变量名的规范要求字母,数字和下划线的组合,必须以下划线和字母开头,不能以数字开头
  等号两边不允许有空格
  尽量见名知意
  名字尽量避开系统命令
  双引号识别特殊变量,单引号不识别变量
[root@test3 ~]# name=xp
[root@test3 ~]# echo "_$name"
_xp
[root@test3 ~]# echo "$name_"

[root@test3 ~]# echo "${name}_"
xp_
[root@test3 ~]# a=xp
[root@test3 ~]# echo "${name}_$a"
xp_xp


使用变量定义路径
[root@test3 ~]# a=/etc/sysconfig/network-scripts/ifcfg-ens33
[root@test3 ~]# cat $a
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=static
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=stable-privacy
NAME=ens33
UUID=c1a2bf5b-733f-464e-bb42-beb2d7ed3be8
DEVICE=ens33
ONBOOT=yes
IPADDR=192.168.23.103
NETMASK=255.255.255.0
GATEWAY=192.168.23.2
DNS1=8.8.8.8

编辑
[root@test3 ~]# vim $a

命令的定义,用``或者$()
[root@test3 ~]# ls /home
ll  www
[root@test3 ~]# a=`ls /home`
[root@test3 ~]# echo $a
ll www
[root@test3 ~]# a=$(ls /home)
[root@test3 ~]# echo $a
ll www

输出年月日时分秒,只要赋值无论怎么输出他的结果都不会变,因为,他就只赋值那一次,只是把当时时间的值赋值给他了,后面并没有在赋值,他的变量就一直保持这赋值的那一秒
[root@test3 ~]# date +%F-%H-%M-%S
2025-01-09-21-54-51
[root@test3 ~]# Time=`date +%F-%H-%M-%S`
[root@test3 ~]# echo $Time
2025-01-09-21-54-59
[root@test3 ~]# echo $Time
2025-01-09-21-54-59
[root@test3 ~]# echo $Time
2025-01-09-21-54-59
[root@test3 ~]# echo $Time
2025-01-09-21-54-59

只要赋值了,哪怕你把这个服务关了他依然会有,因为在你赋值的那一刻这个命令的值就是这个,所以这个变量的值只要不在重新赋值,他就会一直是这个,只有在重新赋值后才会改变
[root@test3 ~]# netstat -tunlp|grep nginx
tcp        0      0 0.0.0.0:8888            0.0.0.0:*               LISTEN      1715/nginx: master  
[root@test3 ~]# a=`netstat -tunlp|grep nginx`
[root@test3 ~]# echo $a
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN 1715/nginx: master                  
[root@test3 ~]# systemctl stop nginx
[root@test3 ~]# echo $a
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN 1715/nginx: master


两种时间需求
1.要求时间不能变化
  数据备份
  mkdir dir_$Time
2.要求时间实时变化
  执行命令的时候不想看结果,需要输出到日志文件中
  直接把他定义成字符串,不解析他,这样每次执行他都会变
  [root@test3 ~]# Time='date +%F-%H-%M-%S'
 [root@test3 ~]# echo $Time
date +%F-%H-%M-%S
[root@test3 ~]# $Time
2025-01-09-22-08-45
[root@test3 ~]# $Time
2025-01-09-22-08-47

shell位置变量

1. $? 上一条命令的执行结果,0为成功,非0失败
[root@test3 ~]# ping www.baidui.com -c1 -W1>/dev/null
[root@test3 ~]# echo $?
0
[root@test3 ~]# ping www.baiduiiiii.com -c1 -W1>/dev/null
ping: www.baiduiiiii.com: Name or service not known
[root@test3 ~]# echo $?
2


2.$n 表示脚本参数的个数,从1开始,$1为第一个参数,$2 为第二个参数
$0 表示脚本的名称 
[root@test3 ~]# cat 1.sh 
#!/bin/bash
echo $1
[root@test3 ~]# bash 1.sh 111
111
[root@test3 ~]# cat 1.sh 
#!/bin/bash
echo $1 $2
[root@test3 ~]# bash 1.sh 111 222
111 222
[root@test3 ~]# cat 1.sh 
#!/bin/bash
echo "Usage $0 [start|restart|stop|status]"
[root@test3 ~]# bash 1.sh 
Usage 1.sh [start|restart|stop|status]

$# 表示脚本传参的个数,可以控制用户传入的参数
[root@test3 ~]# cat 1.sh 
#!/bin/bash
echo $#
[root@test3 ~]# bash 1.sh {1..26}
26


$$ 表示脚本的pid号
$! 表示上一个在后台执行脚本的pid号
[root@test3 ~]# cat 1.sh 
#!/bin/bash
sleep 30
[root@test3 ~]# bash 1.sh &
[1] 2202
[root@test3 ~]# echo $!
2202


[root@test3 ~]# cat 1.sh 
#!/bin/bash
echo $$
sleep 30
[root@test3 ~]# bash 1.sh 
2207
[root@test3 ~]# ps -ef|grep 2207
root       2207   1766  0 10:38 pts/1    00:00:00 bash 1.sh
root       2208   2207  0 10:38 pts/1    00:00:00 sleep 30
root       2229   2211  0 10:38 pts/0    00:00:00 grep --color=auto 2207


$*   获取所有传参的参数
$@   获取所有传参的参数
$_   获取传参的最后一个参数

[root@test3 ~]# cat 1.sh 
#!/bin/bash
echo $*
[root@test3 ~]# bash 1.sh 1 2 3
1 2 3
$@$*是一样的,除了在循环的情况下
$@ 将所有参数视为独立的个体
$* 将所有参数视为一个整体
[root@test3 ~]# cat 1.sh 
#!/bin/bash
echo $@
[root@test3 ~]# bash 1.sh 1 2 3
1 2 3


[root@test3 ~]# cat 1.sh 
#!/bin/bash
echo $1 $2 $3
[root@test3 ~]# bash 1.sh 1 2 3
1 2 3
[root@test3 ~]# echo $_
3

和变量相关的配置文件

1./etc/profile #永久生效,重新连接,重启系统,source文件 
2./root/.bash_profile
3./root/.bash_rc

执行顺序
先执行 1./etc/profile,开机就执行,全局针对所有的用户
     2.  .bash_profile   剩下的三是针对用户的变量
     3.  .bash_rc
     4.   /etc/bashrc

变量传参

传参的三种方式
直接传参
[root@test3 ~]# cat 1.sh 
#!/bin/bash
echo $1 $2 $3
[root@test3 ~]# bash 1.sh 1 2 3
1 2 3

赋值传参
[root@test3 ~]# cat 1.sh 
#!/bin/bash
name=$1
age=$2
echo "姓名: $1"
echo "年龄: $2"
[root@test3 ~]# bash 1.sh 1 2
姓名: 1
年龄: 2

read 读入传参
[root@test3 ~]# read Test
xp
[root@test3 ~]# echo $Test
xp

[root@test3 ~]# cat 1.sh 
#!/bin/bash
read -p 'please input name' name
read -p 'please input age' age
echo $name
echo $age
[root@test3 ~]# bash 1.sh 
please input nameth
please input age11
th
11
-s 是将交互输入的隐藏掉
[root@test3 ~]# cat 1.sh 
#!/bin/bash
read -s -p 'please input name: ' name
echo "你的名字是:$name"
[root@test3 ~]# bash 1.sh 
please input name: 你的名字是:xp

[root@test3 ~]# cat 1.sh 
#!/bin/bash
read -s -p 'please input name: ' name  
echo -e "\t"
echo "你的名字是:$name"
[root@test3 ~]# bash 1.sh 
please input name: 	
你的名字是:xp


变量的子串

1.统计字符串的长度
[root@test3 ~]# echo 'wwww'|wc -L
4
统计文件中最长行的长度
[root@test3 ~]# cat 1.txt 
aa
aaaaa
a
[root@test3 ~]# cat 1.txt |wc -L
5
[root@test3 ~]# echo 'wwww'|awk '{print length}'
4

前面加个#就是统计长度信息
[root@test3 ~]# name=oldboy
[root@test3 ~]# echo ${name}
oldboy
[root@test3 ~]# echo ${#name}
6


笔试题,小于三的输出
[root@test3 ~]# echo I am lihua I am 18
I am lihua I am 18
[root@test3 ~]# for i in `echo I am lihua I am 18` ;do if [[ `echo $i |wc -L` < 3 ]];then echo $i;fi;done
I
am
I
am
18

awk按行去统计
[root@test3 ~]# cat 1.txt 
I
am
I
aaaaa
am
18
[root@test3 ~]# cat 1.txt |awk '{print length}'
1
2
1
5
2
2
[root@test3 ~]# cat 1.txt |awk '{if(length($1)<3)print $1}'
I
am
I
am
18
[root@test3 ~]# echo I am lihua I am 18|xargs -n1
I
am
lihua
I
am
18

awk按列去统计                                             NF最后一列的列号
[root@test3 ~]# echo I am lihua I am 18|awk '{for(i=1;i<=NF;i++)if(length($i)<3)print $i}'
I
am
I
am
18

变量子串的删除
不想要前面的www.
[root@test3 ~]# url=www.baidu.com
[root@test3 ~]# echo $url
www.baidu.com
可以用sed替换
[root@test3 ~]# echo $url|sed 's/www.//g'
baidu.com
可以用# #在前面表示统计字符串长度,在后面表示要删除的内容
[root@test3 ~]# echo ${url#www.}
baidu.com
也可以用正则来表示
[root@test3 ~]# echo ${url#*.}
baidu.com
%表示从后面往前删
[root@test3 ~]# echo ${url%.com}
www.baidu
[root@test3 ~]# a=10.5
[root@test3 ~]# [ $a -eq 10 ]
-bash: [: 10.5: integer expression expected
[root@test3 ~]# [ ${a%.*} -eq 10 ]
表示替换
[root@test3 ~]# echo $url
www.baidu.com
[root@test3 ~]# echo ${url/baidu/sina}
www.sina.com

数值运算

1.expr,只支持整数
[root@test3 ~]# expr 1 + 1
2
[root@test3 ~]# expr 2 \* 3
6
[root@test3 ~]# expr 6 / 3
2


2.$[]
[root@test3 ~]# echo $[10+10]
20
[root@test3 ~]# echo $[10+10*2]
30

3.$(())
[root@test3 ~]# echo $((1+1))
2
[root@test3 ~]# echo $((1*11))
11
[root@test3 ~]# echo $((9/3))
3


4.let
[root@test3 ~]# let a=1+1
[root@test3 ~]# echo $a
2
[root@test3 ~]# let a=2*3
[root@test3 ~]# echo $a
6
自增
[root@test3 ~]# let i++
[root@test3 ~]# echo $i
5
[root@test3 ~]# echo $i
5
[root@test3 ~]# let i++
[root@test3 ~]# echo $i
6
[root@test3 ~]# let i++
[root@test3 ~]# echo $i
7

5.bc,支持小数
[root@test3 ~]# echo 1+2|bc
3
[root@test3 ~]# echo 100+10.5|bc
110.5
xargs -n列数,把多少列变成一整行
[root@test3 ~]# ps -aux|awk  -F " " '{print $5}'|grep -v VSZ|xargs -n1000
125488 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 39056 198572 45772 0 0 0 0 0 0 0 0 0 0 0 0 55532 0 0 69256 26384 58216 195208 474236 168148 199248 21684 117808 612232 126388 110208 0 574284 112900 222740 1618636 1564396 89708 89880 0 0 161512 115684 161512 115680 0 0 89812 0 0 0 155452 113648 112812 108356
[root@test3 ~]# ps -aux|awk  -F " " '{print $5}'|grep -v VSZ|xargs -n1000|sed 's/ /+/g'
125488+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+39056+198572+45772+0+0+0+0+0+0+0+0+0+0+0+0+55532+0+0+69256+26384+58216+195208+474236+168148+199248+21684+117808+612232+126388+110208+0+574284+112900+222740+1618636+1564396+89708+89880+0+0+161512+115684+161512+115680+0+0+89812+0+0+155452+113648+112812+108356+117040
[root@test3 ~]# ps -aux|awk  -F " " '{print $5}'|grep -v VSZ|xargs -n1000|sed 's/ /+/g'|bc
8180716


6.awk
[root@test3 ~]# awk 'BEGIN{print 1+3}'
4
awk计算内存使用的百分比(使用的了/总的*100)
[root@test3 ~]# free
              total        used        free      shared  buff/cache   available
Mem:        6054704      395724     5384164       12008      274816     5416772
Swap:       1048572           0     1048572
[root@test3 ~]# free|awk 'NR==2{print $3/$2*100}'
6.53792


7.python
[root@test3 ~]# python
Python 2.7.5 (default, Nov 14 2023, 16:14:06) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 1+1
2
>>> 3+5
8
>>> 3*5
15

使用三种传参方式,做一个加减乘除的计算器
[root@test3 ~]# cat 1.sh 
#!/bin/bash
read -p 'please input first number: ' num1
read -p 'please input +|-|*|/ ' num3
read -p 'please input second number: ' num2
if [[  `echo $num1|grep .|wc -l` == 1 ]] || [[ `echo $num2|grep .|wc -l` == 1 ]]
    then
        echo $num1 $num3  $num2|bc
else
        expr $num1 $num3  $num2
fi



表达式之文件判断

语法结构
语法1; test -f /etc/hosts # 判断hosts文件是否存在,存在则为真
语法2: [ -f  /etc/hosts ]  # 判断hosts文件是否存在,存在则为真

-f  #判断文件是否存在
-d  #判断目录是否存在
-e  #存在即为真,不管是啥,在就行
-r  #可读为真,一般测试的是属主
-w  #可写为真,一般测试的是属主
-x  #可执行为真,一般测试的是属主
-n  #长度不为0
-z  #长度为0
并且 &&
或者 ||
如果写在if判断里面 -a 是并且 -o是或者
if [[ -f /etc/hosts -a -d /etc ]]
then 
  echo ok
else
  echo no


使用man test可以查看这些参数

      -p FILE
              FILE exists and is a named pipe

       -r FILE
              FILE exists and read permission is granted

       -s FILE
              FILE exists and has a size greater than zero

       -S FILE
              FILE exists and is a socket

       -t FD  file descriptor FD is opened on a terminal

       -u FILE
              FILE exists and its set-user-ID bit is set

       -w FILE
              FILE exists and write permission is granted

       -x FILE
              FILE exists and execute (or search) permission is granted

传参的时候会用
[root@test3 ~]# xx=root
[root@test3 ~]# [ -n $xx ]&& echo 1 || echo 2
1
[root@test3 ~]# [ -z $xx ]&& echo 1 || echo 2
2


判断你输入的是否为整数
#!/bin/bash
read -p 'please input age: ' age
expr 1 + $age > /dev/null  2>&1
[ $? -ne 0 ]&& echo '必须输入整数'

条件表达式

语法结构
  条件表达式都可以使用命令或者变量
[  字符串1 == 字符串2 ]
-eq #相等
-ne #不等于
-gt #大于
-ge #大于等于
-lt #小于
-le #小于等于
-

文件判断

[ -f ]
-d
-e
-r
-w
-x
字符串比对
[ "字符串1" = "字符串2" ]
正则比对
[ "字符串1" = "字符串2" ]


if判断

语法结构
if [ 条件表达式 ]
  then
    执行的命令1
else
    执行命令2
fi

if表达式多分支
if [ 条件表达式1 ]
  then
    执行的命令1
elif [ 条件表达式2 ]
  then 
    执行的命令2
else
    执行命令3
fi

[root@test3 ~]# cat 1.sh 
#!/bin/bash
i=1
if [[ `echo $i` = 2 ]]
  then 
     echo 1
elif [[  `echo $i` = 1 ]]
  then
     echo 2
else
   echo 3
fi  
[root@test3 ~]# bash 1.sh 
2


判断两个数字的大小
[root@test3 ~]# cat 1.sh 
#!/bin/bash
if [[ $# != 2 ]]
 then
    echo "Usage: bash $0  num1 num2" 
    exit 1
fi
expr $1 + $2 &>/dev/null
if [ $? -eq 0 ];then
if [ $1 -gt $2 ]
then
echo "$1 > $2"
elif [ $1 -lt $2 ]
then
echo "$1 < $2"
else
echo "$1 = $2"
fi
else
echo "必须输入整数"
exit
fi



需求:
1.随机生成1-100之间的数字
[root@shell:scripts]#echo $((RANDOM%100+1))
2.和用户交互,让用户输入猜随机数
3.提示用户猜大了或者猜小了
4.用户猜对了则提示中奖
5.输出中奖号码
6.只能有5次机会,显示结果
[root@test3 ~]# cat 1.sh
#!/bin/bash
i=0
num1=`echo $((RANDOM%100+1))`
echo $num1
while true
do
let i++
if [[ $i -gt 5 ]]
  then
    echo '你已经超过5次了'
   exit 1
fi
read -p 'please input a num: ' num2
if [[  $num1 > $num2 ]]
   then
     echo '你猜小了'
elif [[  $num1 < $num2 ]]
   then
      echo '你猜大了'
else
     echo '你猜对了'
    exit 0
fi
done

if菜单

[root@test3 ~]# cat 1.sh 
#!/bin/bash
echo '1.查看系统磁盘'
echo '2.查看系统负载'
echo '3.查看系统内存'
echo '4.查看系统ip'
read -p 'please input num[1|2|3|4]' num1
if [[ $num1 = 1 ]]
  then
     df -h /|awk 'NR==2{print $(NF-1)}'
elif [[ $num1 = 2 ]]
then
 uptime |awk -F ":" '{print $(NF)}'
elif [[ $num1 = 3 ]]
then
   free -h
elif [[ $num1 = 4 ]]
then
  hostname -I
else
 echo '请输入1-4之间的整数'
 fi

这个是带颜色的
#!/bin/bash
fun(){
echo -e "\t\t\t\t\t\033[34m1.查看系统内存\033[0m"
echo -e "\t\t\t\t\t\033[34m2.查看系统磁盘\033[0m"
echo -e "\t\t\t\t\t\033[34m3.查看系统负载\033[0m"
echo -e "\t\t\t\t\t\033[34m4.查看系统IP\033[0m"
echo -e "\t\t\t\t\t\033[34m5.退出脚本\033[0m"
}
#调用函数
fun


多条件判断:
[root@shell:scripts]#cat menu.sh
#!/bin/bash
fun(){
echo -e "\t\t\t\t\t\033[34m1.f查看系统内存
\033[0m"
echo -e "\t\t\t\t\t\033[34m2.d查看系统磁盘
\033[0m"
echo -e "\t\t\t\t\t\033[34m3.u查看系统负载
\033[0m"
echo -e "\t\t\t\t\t\033[34m4.i查看系统IP\033[0m"
echo -e "\t\t\t\t\t\033[34m5.q退出脚本\033[0m"
echo -e "\t\t\t\t\t\033[34m6.h菜单信息\033[0m"
}
fun
while true
do
read -p "请输入查看信息的编号[6帮助]: " num
#只能输入1-6
if [ $num = 1 -o $num = "f" ];then    -eq只是用于整数比较,如果是有字符串必须用 = 要不然会报错
free -h
elif [ $num = 2 -o $num = "d" ];then
df -h
elif [ $num = 3 -o $num = "u" ];then
uptime
elif [ $num = 4 -o $num = "i" ];then
curl cip.cc
elif [ $num = 5 -o $num = "q" ];then
exit
elif [ $num = 6 -o $num = "h" ];then
clear
fun
fi
done

二级菜单

二级菜单
[root@shell:scripts]#cat menu2.sh
#!/bin/bash
echo -e "\033[34m1.Nginx\033[0m"
echo -e "\033[34m2.MySQL\033[0m"
echo -e "\033[34m3.PHP\033[0m"
read -p "请输入要安装软件的编号: " num
if [ $num -eq 1 ];then
echo -e "\033[35m1.Nginx-1.20\033[0m"
echo -e "\033[35m2.Nginx-1.26\033[0m"
read -p "请输入要安装的nginx的版本号: " num1
if [ $num1 -eq 1 ];then
echo "配置nginx1.20的仓库"
echo "yum -y install nginx....."
elif [ $num1 -eq 2 ];then
echo "配置Nginx1.26的仓库"
echo "yum -y install nginx....."
fi
elif [ $num -eq 2 ];then
echo -e "\033[35m1.MySQL-5.5\033[0m"
echo -e "\033[35m1.MySQL-8.0\033[0m"
fi
[root@shell:scripts]#cat menu2.sh
#!/bin/bash
while true
do
fun1(){
echo -e "\t\t\t\t\t\033[34m1.Nginx\033[0m"
echo -e "\t\t\t\t\t\033[34m2.MySQL\033[0m"
echo -e "\t\t\t\t\t\033[34m3.PHP\033[0m"
}
fun1
Ngx(){
echo -e "\t\t\t\t\t\033[35m1.Nginx-
1.20\033[0m"
echo -e "\t\t\t\t\t\033[35m2.Nginx-
1.26\033[0m"
echo -e "\t\t\t\t\t\033[35m3.显示菜单信息
\033[0m"
echo -e "\t\t\t\t\t\033[35m4.返回上一级菜单
\033[0m"
}
read -p "请输入要安装软件的编号: " num
if [ $num -eq 1 ];then
Ngx
while true
do
read -p "请输入要安装的nginx的版本号[3菜单]: "
num1
if [ $num1 -eq 1 ];then
echo "配置nginx1.20的仓库"
echo "yum -y install nginx....."
elif [ $num1 -eq 2 ];then
echo "配置Nginx1.26的仓库"
echo "yum -y install nginx....."
elif [ $num1 -eq 3 ];then
Ngx
elif [ $num1 -eq 4 ];then
break                                            #break只是跳出里面的循环,并不会跳出外面的循环,相当于重新执行了一边脚本
fi
done
elif [ $num -eq 2 ];then
echo -e "\033[35m1.MySQL-5.5\033[0m"
echo -e "\033[35m1.MySQL-8.0\033[0m"
fi
done

脚本调用

[root@test3 ~]# cat 1.sh 
#!/bin/bash
fun(){
echo -e "\t\t\t\t\t\033[34m1.查看系统内存\033[0m"
echo -e "\t\t\t\t\t\033[34m2.查看系统磁盘\033[0m"
echo -e "\t\t\t\t\t\033[34m3.查看系统负载\033[0m"
echo -e "\t\t\t\t\t\033[34m4.查看系统IP\033[0m"
echo -e "\t\t\t\t\t\033[34m5.退出脚本\033[0m"
}
fun1(){
echo hehe
}

source一下,就可以调用了
[root@test3 ~]# source 1.sh 
[root@test3 ~]# fun
					1.查看系统内存
					2.查看系统磁盘
					3.查看系统负载
					4.查看系统IP
					5.退出脚本



调用函数
[root@test3 ~]# cat 2.sh 
#!/bin/bash
[ -f /root/1.sh ] && . /root/1.sh
fun
fun1


for循环

for 循环语法
for 变量名称 in [取值列表 数字  字符串 序列 命令]
do 
   命令集合
done


列1。for循环字符串
[root@test3 ~]# cat for.sh 
#!/bin/bash
for i in a b c d e
do
 echo $i
done
[root@test3 ~]# bash for.sh 
a
b
c
d
e
   
控制循环的次数 
[root@test3 ~]# cat for.sh 
#!/bin/bash
for i in a b c
do
 echo aaa
done
[root@test3 ~]# bash for.sh 
aaa
aaa
aaa


for循环支持数列
[root@test3 ~]# cat for.sh 
#!/bin/bash
for i in {1..10} 
do
 echo $i
done
[root@test3 ~]# bash for.sh 
1
2
3
4
5
6
7
8
9
10

字符串拼接
[root@test3 ~]# cat for.sh 
#!/bin/bash
for i in {1..3} 
do
 echo xp$i
done
[root@test3 ~]# bash for.sh 
xp1
xp2
xp3


面试题:测一个网段有哪些通有哪些不通,通的输出到屏幕
注意:超过两次重复调用内容,我们都要把他设置成变量
[root@test3 ~]# cat 1.sh 
#!/bin/bash
for i in {1..254}
do
 {
   IP=172.17.0.$i
   ping -c1 -W1 $IP > /dev/null 2>'$1'
   if [[ $? = 0 ]]
     then
       echo $IP
     fi
     } &  #表示并发,所有的一起ping 不再是一个一个ping了
 done
wait   #等待上面所有并发执行完成之后在继续往下
echo 111



从1加到100
[root@test3 ~]# cat 1.sh 
#!/bin/bash
for i in {1..100}
do
  count=$[$count+$i]   #$count第一次默认是0,空值
done
echo $count
[root@test3 ~]# bash 1.sh 
5050


for循环取值列表,按照命令的方式
[root@test3 ~]# cat 1.sh 
#!/bin/bash
for i in `cat /etc/hosts`
do
echo $i
done
[root@test3 ~]# bash 1.sh 
127.0.0.1
localhost
localhost.localdomain
localhost4
localhost4.localdomain4
::1
localhost
localhost.localdomain
localhost6
localhost6.localdomain6
192.168.23.102
registry
192.168.23.103
th.com
zr.com


循环不带空格的文件
[root@test3 ~]# head -3 /etc/passwd > 1.txt
[root@test3 ~]# cat 1.txt 
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@test3 ~]# cat 1.sh
#!/bin/bash
for i in `cat /etc/1.txt`
do
echo $i
done
[root@test3 ~]# bash 1.sh    #按行输出
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

[root@test3 ~]# cat 1.sh 
#!/bin/bash
a=0
for i in `cat /root/1.txt`
do
   let a++
done
echo "当前 $a 行"

[root@test3 ~]# bash 1.sh 
当前 3

流程控制 case

case的语法结构
case 变量 in 
    匹配序列1)
        执行的命令集合
    ;;
    匹配序列2)
        执行的命令集合
    ;;
    匹配序列3)
        执行的命令集合
    ;;
    *)
       echo "Usage $0 [xx|yy]" 
esac

实列1
[root@test3 ~]# cat 1.sh 
#!/bin/bash
case $1 in 
       shell)
          echo 'shell......'
        ;;
       mysql)
          echo 'mysql......'
        ;;
       docker)
          echo 'docker.....'
        ;;
       *)
          echo "Usage $0 [shell|mysql|docker]"
esac
[root@test3 ~]# bash 1.sh docker
docker.....
 

便利目录	
[root@test3 ~]# cat 1.sh 
#!/bin/bash
#!/bin/bash
echo -e "\t\t\t\t\t\033[34m1.f查看系统内存
\033[0m"
echo -e "\t\t\t\t\t\033[34m2.d查看系统磁盘
\033[0m"
echo -e "\t\t\t\t\t\033[34m3.u查看系统负载
\033[0m"
echo -e "\t\t\t\t\t\033[34m4.i查看系统IP\033[0m"
echo -e "\t\t\t\t\t\033[34m5.q退出脚本\033[0m"
echo -e "\t\t\t\t\t\033[34m6.h菜单信息\033[0m"
read -p "请输入查看系统信息的编号: " num
case $num in
     1|f)
        free -h
     ;;
     2|d)
        df -h
     ;;
     3|u)
        uptime
     ;;
     *)
        echo "Usage: $0 [1|f]"
esac



写一个nginx启动脚本
1.nginx启动有两种
  systemctl restart nginx
2.绝对路径
  /usr/sbin/nginx -s reload
  /usr/sbin/nginx -s stop && sleep 1 && /usr/sbin/nginx 重启

[root@test3 ~]# cat 1.sh 
#!/bin/bash
case $1 in
     start)
       /usr/sbin/nginx
     ;;
     stop)
      /usr/sbin/nginx -s stop
     ;;
    restart)
     /usr/sbin/nginx -s stop && sleep 1 && /usr/sbin/nginx
    ;;
    reload)
     /usr/sbin/nginx -s reload
    ;;
    status)
      Port=`netstat -tunlp|grep nginx|awk '{print $4}'`
      echo $Port
    ;;
    *)
     echo "Usage $0 [start|stop|restart|reload|status]"
esac


加判断
[root@test3 ~]# cat 1.sh 
#!/bin/bash
case $1 in
     start)
       /usr/sbin/nginx
       if [[ $? == 0 ]]
         then
           echo "$1 成功"
         else
           echo "$1 失败"
       fi
     ;;
     stop)
      /usr/sbin/nginx -s stop
        if [[ $? == 0 ]]
         then
           echo "$1 成功"
         else
           echo "$1 失败"
       fi
     ;;
    restart)
     /usr/sbin/nginx -s stop && sleep 1 && /usr/sbin/nginx
     if [[ $? == 0 ]]
       then
         echo "$1 成功"
      else
         echo "$1 失败"
     fi
    ;;
    reload)
     /usr/sbin/nginx -s reload
    if [[ $? == 0 ]]
     then
       echo "$1 成功"
    else
       echo "$1 失败"
     fi
    ;;
    status)
      Port=`netstat -tunlp|grep nginx|awk '{print $4}'`
      echo $Port
    ;;
    *)
     echo "Usage $0 [start|stop|restart|reload|status]"
esac




写成函数
#!/bin/bash
fun(){
    if [[ $? == 0 ]]
     then
       echo "$1 成功"
    else
       echo "$1 失败"
     fi

}
case $1 in
     start)
       /usr/sbin/nginx
       fun
     ;;
     stop)
      /usr/sbin/nginx -s stop
     fun
     ;;
    restart)
     /usr/sbin/nginx -s stop && sleep 1 && /usr/sbin/nginx
     fun
    ;;
    reload)
     /usr/sbin/nginx -s reload
     fun
    ;;
    status)
      Port=`netstat -tunlp|grep nginx|awk '{print $4}'`
      echo $Port
    ;;
    *)
     echo "Usage $0 [start|stop|restart|reload|status]"
esac
执行的时候你会发现传参丢了,因为函数里面不认识$1 
[root@test3 ~]# bash 1.sh stop
 成功
更改如下,在fun后面加一个$1即可
[root@test3 ~]# cat 1.sh 
#!/bin/bash
fun(){
    if [[ $? == 0 ]]
     then
       echo "$1 成功"
    else
       echo "$1 失败"
     fi

}
case $1 in
     start)
       /usr/sbin/nginx
       fun $1
     ;;
     stop)
      /usr/sbin/nginx -s stop
     fun $1
     ;;
    restart)
     /usr/sbin/nginx -s stop && sleep 1 && /usr/sbin/nginx
     fun $1
    ;;
    reload)
     /usr/sbin/nginx -s reload
     fun $1
    ;;
    status)
      Port=`netstat -tunlp|grep nginx|awk '{print $4}'`
      echo $Port
    ;;
    *)
     echo "Usage $0 [start|stop|restart|reload|status]"
esac


脚本自带函数,他会自动的在右边生成ok或者false并且带颜色,在此之前需要source /etc/init.d/functions 
[root@test3 ~]# source /etc/init.d/functions 
[root@test3 ~]# ac
accessdb      aclocal       aclocal-1.13  action        
[root@test3 ~]# action xx  /bin/true
xx                                                         [  OK  ]
[root@test3 ~]# action xx  /bin/false
xx                                                         [FAILED]


再次更改
[root@test3 ~]# cat 1.sh 
#!/bin/bash
if [[  -f /etc/init.d/functions ]]
   then
      source /etc/init.d/functions
fi
fun(){
    if [[ $? == 0 ]]
     then
       action "$1"   /bin/true
    else
       action "$1"   /bin/false
     fi

}
case $1 in
     start)
       /usr/sbin/nginx
       fun $1
     ;;
     stop)
      /usr/sbin/nginx -s stop
     fun $1
     ;;
    restart)
     /usr/sbin/nginx -s stop && sleep 1 && /usr/sbin/nginx
     fun $1
    ;;
    reload)
     /usr/sbin/nginx -s reload
     fun $1
    ;;
    status)
      Port=`netstat -tunlp|grep nginx|awk '{print $4}'`
      echo $Port
    ;;
    *)
     echo "Usage $0 [start|stop|restart|reload|status]"
esac

此时输出就带颜色了
[root@test3 ~]# bash 1.sh start
start                                                      [  OK  ]



jumpserver跳板机脚本

jumpserver服务段 test3 192.168.23.103
客户端           test2 192.168.23.102
客户端           test2 192.168.23.101


[root@test3 ~]# cat 1.sh 
#!/bin/bash
IP='192.168.23.102'
nfs='192.168.23.101'
echo -e "\t\033[34m1.$IP\033[0m"
echo -e "\t\033[34m2.$nfs\033[0m"
read -p 'Please input  number you wish to connect to.: ' num1
case $num1 in
     1)
       ssh $IP
     ;;
     2)
       ssh $nfs
     ;;
     *)
      echo "Usage $0 [1|2]"
esac
[root@test3 ~]# bash 
[root@test3 ~]# bash 1.sh 
	1.192.168.23.102
	2.192.168.23.101
Please input  number you wish to connect to.: 1
Last login: Sat Jan  4 21:36:21 2025 from 192.168.23.1
[root@test2 ~]# exit
此时退出他就直接退回到原本的机器上了,既然能退回到原本的住机器上,那我为什么不能直接ssh连呢,这将是致命的

框架,
#!/bin/bash
IP='192.168.23.102'
nfs='192.168.23.101'
fun() {
echo -e "\t\033[34m1.$IP\033[0m"
echo -e "\t\033[34m2.$nfs\033[0m"
echo -e "\t\033[34m3.meau\033[0m"
}
fun
while true
do
read -p 'Please input  number you wish to connect to.(3 is help): ' num1
case $num1 in
     1)
       ssh $IP
     ;;
     2)
       ssh $nfs
     ;;
     3)
      fun
     ;;
     *)
      echo "Usage $0 [1|2]"
esac
done



----------------------------------------------------------------------------
完善jumpserver跳板机
1.运维和开发是不是属于不同的账号
2.运维和开发登录跳板机输入密码
3.开发的菜单和运维的不同
 主菜单
    1.运维
    2.开发
  子菜单
     1.显示运维可以连接的服务器信息
     2.显示开发可以连接的服务器的信息
 =====================菜单的框架============================================
 [root@test3 ~]# cat 1.sh 
#!/bin/bash
IP='192.168.23.102'
nfs='192.168.23.101'
fun1(){
echo -e "\t\033[34m1.ops\033[0m"
echo -e "\t\033[34m2.dev\033[0m"
}
ops() {
echo -e "\t\033[34m1.$IP\033[0m"
echo -e "\t\033[34m2.$nfs\033[0m"
echo -e "\t\033[34m3.meau\033[0m"
}
dev() {
echo -e "\t\033[34m1.$nfs\033[0m"
echo -e "\t\033[34m2.meau\033[0m"
}
while true
do
fun1
read -p '请选择你的角色编号:' num1
case $num1 in 
        1)
          ops
          while true
           do
          read -p '请输入你要连接的主机编号:' num2
          if [[  $num2 -eq 1 ]]
            then
              ssh $IP
          elif [[  $num2 -eq 2 ]]
            then
              ssh $nfs
          elif [[  $num2 -eq 3 ]]
            then
               ops
          else
            echo '只能输入[1|2|3]'
           break
	  fi
         done
        ;; 
        2)
          dev
          while true
          do
          read -p '请输入你要连接的主机编号:' num3
  	  if [[  $num3 -eq 1 ]]
            then
              ssh $nfs
          elif [[  $num3 -eq 2 ]]
            then
               dev
          else
              echo '请输入[1|2] '
              break
         fi              
        done
	;;
        *)
          echo "Usage $0 [1|2]"
esac
done
===========================================================================================


通过邮箱找回密码(难道和简单的两种)
简单的可以把密码写入到一个文件中,通过找提取密码来对比,也可以写死
[root@test3 ~]# cat pass.txt 
oldboy   123456
[root@test3 ~]# grep -w oldboy pass.txt |awk '{print $2}'
123456
写死
[root@test3 ~]# cat 1.sh
#!/bin/bash
IP='192.168.23.102'
nfs='192.168.23.101'
fun1(){
echo -e "\t\033[34m1.ops\033[0m"
echo -e "\t\033[34m2.dev\033[0m"
}
ops() {
echo -e "\t\033[34m1.$IP\033[0m"
echo -e "\t\033[34m2.$nfs\033[0m"
echo -e "\t\033[34m3.meau\033[0m"
}
dev() {
echo -e "\t\033[34m1.$nfs\033[0m"
echo -e "\t\033[34m2.meau\033[0m"
}
while true
do
fun1
read -p '请选择你的角色编号:' num1
case $num1 in 
        1)
         i=0
         while true
         do
         read -s -p '请输入密码:' pass
            let i++
            if [[  $i = 5 ]]
              then
                echo '你已经输错五次了,请30s后尝试'
               # break
                sleep 30
                unset i
                # exit
             fi
             if [[  "$pass" = '123456' ]]
              then 
                break
              else
                echo -e "\t"
                echo '请输入正确的密码'
               # break
                 #continue
             fi
          done
          echo -e "\t"
          ops
          while true
           do
          read -p '请输入你要连接的主机编号:' num2
          if [[  $num2 -eq 1 ]]
            then
              ssh $IP
          elif [[  $num2 -eq 2 ]]
            then
              ssh $nfs
          elif [[  $num2 -eq 3 ]]
            then
               ops
          else
            echo '只能输入[1|2|3]'
           break
	  fi
         done
        ;; 
        2)
          read -p '请输入密码:' pass


错误四次发邮箱验证码
if [ $i = 4 ];then
read -p "尝试次数过多,请输入邮箱地址找回密码: " MA
if [ $MA = "2334537366@qq.com"];then
echo $RANDOM|md5sum|cut -c1-8>p.txt
mail -s '验证码' "2334537366@qq.com" <p.txt
read -p "请输入验证码: " a
[ $a = `cat p.txt` ] && mail -s '账号密码请记牢固' "2334537366@qq.com" <pass.txt
fi
fi
 

不能ctrl + c,
在我们执行ctrl + c 或者 +z +q等等的时候,他都是通过 trap 给系统发送终止信号
trap "" HUP TSTP INT  禁止ctrl + c ctrl + z ctrl + q,就是不允许你在脚本里面退出
trap "echo 别乱按,小心爆炸" HUP TSTP INT   

while循环

while语法
while 条件表达式   #为真才执行
do  
   执行的命令集合
done[root@test3 ~]# cat 2.sh 
#!/bin/bash
while [ 1 -eq 1 ]  或者true
do
   echo hehe
  sleep 1
done
[root@test3 ~]# bash 2.sh 
hehe
hehe

使用while从1加到100
[root@test3 ~]# bash 2.sh 
5050
[root@test3 ~]# cat 2.sh 
#!/bin/bash
i=1
sum=0
while [ $i -le 100 ]
do
  sum=$[$sum+$i]
  let i++
done
echo $sum
[root@test3 ~]# bash 2.sh 
5050


read是读
line是行 所以说while是按照一行一行的读取文件,for循环读取文件是按照空格来读取
line是自定义变量,叫啥都行
while read line
do

done < 文件路径
[root@test3 ~]# cat 1.txt 
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@test3 ~]# vim 2.sh
[root@test3 ~]# cat 2.sh
#!/bin/bash
while read line
do
echo $line

done</root/1.txt
[root@test3 ~]# bash 2.sh 
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

分别用while和for来创建用户 已经设置对应的密码
[root@test3 ~]# cat 1.txt 
zs  oldboy123
xp  oldboy456
sh  oldboy789
for循环实例
[root@test3 ~]# cat 2.sh 
#!/bin/bash
for  i in `cat 1.txt |awk '{print $1}'`
 do
pass=`cat 1.txt|grep $i|awk '{print $2}'`
useradd $i 
echo $pass|passwd --stdin $i
done
[root@test3 ~]# bash 2.sh 
Changing password for user zs.
passwd: all authentication tokens updated successfully.
Changing password for user xp.
passwd: all authentication tokens updated successfully.
Changing password for user sh.
passwd: all authentication tokens updated successfully.



while循环取列
[root@test3 ~]# cat 2.sh 
#!/bin/bash
while read a b 
do
echo $a

done<1.txt

[root@test3 ~]# bash 2.sh 
zs
xp
sh

[root@test3 ~]# bash 2.sh 
Changing password for user zs.
passwd: all authentication tokens updated successfully.
Changing password for user xp.
passwd: all authentication tokens updated successfully.
Changing password for user sh.
passwd: all authentication tokens updated successfully.


[root@test3 ~]# cat 2.sh 
#!/bin/bash
while read a b 
do
useradd $a
echo $b|passwd --stdin $a
done<1.txt



while read
do

done < 也支持命令

[root@test3 ~]# cat 2.sh 
#!/bin/bash
while read a b 
do
echo  $a
done < <( cat 1.txt )
[root@test3 ~]# bash 2.sh 
zs
xp
sh


shell命令行来执行循环

[root@test3 ~]#  for i in `echo {1..5}`;do echo $i;done
1
2
3
4
5


创建用户
[root@test3 ~]#  for i in `echo {1..5}`;do useradd test$i;done
[root@test3 ~]# tail -n5 /etc/passwd
test1:x:1004:1004::/home/test1:/bin/bash
test2:x:1005:1005::/home/test2:/bin/bash
test3:x:1006:1006::/home/test3:/bin/bash
test4:x:1007:1007::/home/test4:/bin/bash
test5:x:1008:1008::/home/test5:/bin/bash


删除用户
[root@test3 ~]#  for i in `echo {1..5}`;do userdel -r test$i;done

函数

1.完成特定功能的代码块
2.重复复用
3.类似变量,先定义在调用

1)函数的定义
2)函数的传参
3)函数的变量
4)函数的返回值


1)函数的定义
三种定义方式
[root@test3 ~]# cat 2.sh 
#!/bin/bash
fun1(){
  echo "函数的第一种定义方式"
}
function fun2 {             #一定要有空格
 echo "函数的第二种定义方式"
}
function fun3(){
 echo "函数的第三种定义方式"
}
fun1
fun2
fun3
[root@test3 ~]# bash 2.sh 
函数的第一种定义方式
函数的第二种定义方式
函数的第三种定义方式


2)函数的传参
[root@test3 ~]# cat 2.sh 
#!/bin/bash
fun1(){
   if [  -f $1 ] 
    then
     echo "$1 Ok"
   else 
      echo "$1 on"
 fi
}
fun1
[root@test3 ~]# bash 2.sh /etc/passwd
 Ok
 [root@test3 ~]# bash 2.sh /etc/passwdwwwww
 Ok


你会发现$1参数没读出来,传一个不存在的也是ok,因为传参没传进去

[root@test3 ~]# cat 2.sh 
#!/bin/bash
fun1(){
   if [  -f $1 ] 
    then
     echo "$1 Ok"
   else 
      echo "$1 on"
 fi
}
fun1 /etc/hosts  /etc/passwd  #函数的传参他是写在调用函数的后面,/etc/hosts 会传给$1  /etc/passwd会传给$2
[root@test3 ~]# bash 2.sh 
/etc/hosts Ok


既然它可以这样,我们我们可以使用脚本的传参
[root@test3 ~]# cat 2.sh 
#!/bin/bash
fun1(){
   if [  -f $1 ] 
    then
     echo "$1 Ok"
   else 
      echo "$1 on"
 fi
}
fun1 $1 $2 
只接受第一个
[root@test3 ~]# bash 2.sh /etc/hosts /etc/passwd
/etc/hosts Ok


函数可以接受的传参(全局传参)#这种方法是最常用的
[root@test3 ~]# cat 2.sh 
#!/bin/bash
file=$1
fun1(){
   if [  -f $file ] 
    then
     echo "$file Ok"
   else 
      echo "$file on"
 fi
}
fun1
[root@test3 ~]# bash 2.sh /etc/pssswd
/etc/pssswd on


3)函数的变量
[root@test3 ~]# cat 2.sh 
#!/bin/bash
file=$1
fun1(){
   if [  -f $file ] 
    then
     echo "$file Ok"
   else 
      echo "$file on"
 fi
}
fun1
echo $file                   #重复调用
[root@test3 ~]# bash 2.sh  /etc/passwd
/etc/passwd Ok
/etc/passwd



[root@test3 ~]# cat 2.sh 
#!/bin/bash
file=$1
fun1(){
local name=hehe    #local他只在函数体里面生效,除了函数体就调用不到了,无论下面怎么调用都是控制,要想调用,把echo $name塞到函数里面,可以在多个函数体使用相同变量,并且不会重复的赋值
}
fun1
echo $name
fun2(){
local name=xx
}
fun2
echo $name

[root@test3 ~]# bash 2.sh 

[root@test3 ~]# cat 2.sh 
#!/bin/bash
file=$1
fun1(){
local name=hehe
echo $name
}
fun2(){
local name=xx
echo $name
}
fun1
fun2
[root@test3 ~]# bash 2.sh 
hehe
xx



4)函数的返回值
$?
函数退出的时候默认只输入exit 默认返回值是0 exit 1默认返回值是1,可以指定1-255
比如说脚本上千行,上千行突然退出了,你给他一个返回值,我们可以直接根据这个返回值来定位问题


在函数中使用return作为返回值
[root@test3 ~]# cat 2.sh 
#!/bin/bash
file=$1
fun1(){
  if [  -f $1 ]
    then
      return 100     #不能用exit 用exit就直接退出了,就不是返回值了,想要做多次返回的时候用return
   else
     return 50
  fi
}
fun1
[root@test3 ~]# bash 2.sh /etc/passwd
[root@test3 ~]# echo $?
100





[root@test3 ~]# cat 2.sh 
#!/bin/bash
fun1(){
  if [  -f $1 ]
    then
      return 100
   else
     return 50
  fi
}
fun1 $1
if [ $? -eq 100  ];then
   echo '文件存在'
elif [  $? -eq 50 ];then
   echo '文件不存在'
fi

[root@test3 ~]# bash 2.sh /etc/passwd
文件存在
[root@test3 ~]# bash 2.sh /etc/passwddddd    #输入不存在的时候他没有返回值,因为他拿的是上一个if判断的返回值
[root@test3 ~]# bash -x  2.sh /etc/passwddddd
+ fun1 /etc/passwddddd
+ '[' -f /etc/passwddddd ']'
+ return 50
+ '[' 50 -eq 100 ']'
+ '[' 1 -eq 50 ']'

解决:
先把$?的值给赋值了就可以了
[root@test3 ~]# cat 2.sh 
#!/bin/bash
fun1(){
  if [  -f $1 ]
    then
      return 100
   else
     return 50
  fi
}
fun1 $1
Re=$?
if [ $Re -eq 100  ];then
   echo '文件存在'
elif [  $Re -eq 50 ];then
   echo '文件不存在'
fi
[root@test3 ~]# bash 2.sh /etc/passwd
文件存在
[root@test3 ~]# bash 2.sh /etc/passwdddd
文件不存在

数组

数组分为普通数组和关联数组
语法格式:
    数组名称[元素的名字]=元素的值
    数组名称[也可以叫索引]=索引的值
    变量一个变量只能对应一个值,数组一个变量可以放多个值
变量的定义方式
箱子=苹果

数组的定义方式
箱子[小盒子1]=苹果
箱子[小盒子2]=香蕉
箱子[小盒子3]=黄瓜

数组的定义
普通数组,只能用数字作为元素的名称
1.使用索引的名称来定义
[root@test3 ~]# array[1]=shell
[root@test3 ~]# array[2]=mysql
[root@test3 ~]# array[3]=nginx
查看索引
[root@test3 ~]# echo ${array[1]}
shell
[root@test3 ~]# echo ${array[2]}
mysql
[root@test3 ~]# echo ${array[3]}
nginx
[root@test3 ~]# echo ${array[*]}
shell mysql nginx
[root@test3 ~]# echo ${array[@]}
shell mysql nginx
也可以用
[root@test3 ~]# declare -a|tail -n 1#查看已经定义的普通数组
declare -a array='([1]="shell" [2]="mysql" [3]="nginx")'
想看有多少索引
[root@test3 ~]# echo ${!array[@]}
1 2 3
第二种定义方式
[root@test3 ~]# unset array
[root@test3 ~]# array=(11 22 33)
[root@test3 ~]# echo ${array[0]}
11
[root@test3 ~]# echo ${array[*]}
11 22 33
[root@test3 ~]# echo ${!array[*]}
0 1 2

第三种定义方式,混合式定义
[root@test3 ~]# array=([5]=aa bb [10]=cc dd)
[root@test3 ~]# echo ${array[*]}
aa bb cc dd
[root@test3 ~]# echo ${!array[*]}
5 6 10 11

第四种,命令方式定义
[root@test3 ~]# unset array
[root@test3 ~]# ls
1      1.yaml      anaconda-ks.cfg              ceshi            for.sh      installed_nginx.yml  nginx           pass.txt
$1     1.yml       apache-tomcat-9.0.98         create_user.yml  hosts       install_nginx.yml    nginx.conf      passwd
1.py   2.conf      apache-tomcat-9.0.98.tar.gz  dead.letter      httpd       logs                 nginx_copy.yml  repositories
1.sh   2.sh        blobs                        django           index.html  manifest.json        nohup.out       rsyncd.conf
1.txt  all.tar.gz  -C                           eluosi.sh        index.json  mybuild              oci-layout      th
[root@test3 ~]# array=(`ls`)   #他会把当前目录下所有的ls能查看的全部都定义成索引
[root@test3 ~]# echo ${array[*]}
1 $1 1.py 1.sh 1.txt 1.yaml 1.yml 2.conf 2.sh all.tar.gz anaconda-ks.cfg apache-tomcat-9.0.98 apache-tomcat-9.0.98.tar.gz blobs -C ceshi create_user.yml dead.letter django eluosi.sh for.sh hosts httpd index.html index.json installed_nginx.yml install_nginx.yml logs manifest.json mybuild nginx nginx.conf nginx_copy.yml nohup.out oci-layout pass.txt passwd repositories rsyncd.conf th
[root@test3 ~]# 


案列:测试不连续的ip地址是否在线
192.168.23.102
10.0.0.7
114.114.114.114
223.5.5.5
8.8.8.8  测试这些ip地址是否在线
[root@test3 ~]# for i in `echo ${array[*]}`;do ping -c1 -W1 $i > /dev/null 2>'$1';if [ $? -eq 0 ];then echo $i;fi;done
192.168.23.102
223.5.5.5
8.8.8.8

[root@test3 ~]# cat 3.sh
#!/bin/bash
ip=(
192.168.23.102
10.0.0.7
114.114.114.114
223.5.5.5
8.8.8.8
)
#echo ${ip[*]}
for i in `echo ${ip[*]}`
do
  ping -c1 -W1 $i > /dev/null 2>'$1'
  if [ $? -eq 0 ];then 
    echo $i
  fi
done
[root@test3 ~]# bash 3.sh
192.168.23.102
223.5.5.5
8.8.8.8
[root@tes



关联数组,可以用数字和字符串作为元素的名称
[root@test3 ~]# unset array
[root@test3 ~]# array[index1]=shell
[root@test3 ~]# array[index2]=mysql
[root@test3 ~]# array[index3]=nginx
[root@test3 ~]# echo ${array[*]}
nginx  #为什么就一个呢,因为他不认识字符串,不认识它默认就是0,第一个他把shell赋值给0,第二个他把mysql赋值给0,第三个他把nginx赋值给0,所以最后才是nginx 

所以说关联数组需要声明
[root@test3 ~]# unset array
[root@test3 ~]# declare -A array   #需要先声明才行
[root@test3 ~]# array[index1]=shell
[root@test3 ~]# array[index2]=mysql
[root@test3 ~]# array[index3]=nginx
[root@test3 ~]# echo ${array[*]}
shell mysql nginx
[root@test3 ~]# declare -A |tail -1
declare -A array='([index1]="shell" [index2]="mysql" [index3]="nginx" )'

案例1.统计字母出现的次数
[root@test3 ~]# unset array
[root@test3 ~]# declare -A array
[root@test3 ~]# let array[i]++  #====let array[i] = array[i]+1
[root@test3 ~]# #let array[苹果]++ 遇到苹果就加一次,最后的总数就是array[苹果]


[root@test3 ~]# cat 2.txt 
f
m
f
f
x
[root@test3 ~]# cat array.sh 
#!/bin/bash
declare -A Te
while read line
do
  let Te[$line]++
done< 2.txt
for i in ${!Te[*]}
do
   echo $i 出现了 ${Te[$i]}done
[root@test3 ~]# vim array.sh
[root@test3 ~]# bash array.sh 
f 出现了 3 次
m 出现了 1 次
x 出现了 1

流程控制语句的三个

exit 退出整个脚本
break 跳出循环,break 1 退出1层,break 2 退出两层,以此类推
continue 忽略本次剩余循环代码,重新循环

expect脚本

交互脚本expect
1.简单实现免交互登录
脚本开头一般以 #!/usr/bin/expect 开头
常用后缀
expect脚本常常以.exp或者.ex结束

expect主要命令
  1.spawn新建一个进程,这个进程的交互由expect控制
  2.expect等待接受进程返回的字符串,直到超时时间,根据规则决定下一步操作
  3.send发送字符串给expect控制的进程
  4.set设置变量为某个值,在shell种 name=oldboy 在expect种 set name oldboy
  5.exp_continue 重新执行expect命令分支
  6.[lindex $argv 0] 获取expect脚本的第一个参数
  7.[lindex $argv 1] 获取expect脚本的第二个参数
  8.set timeout -1 设置超时方式为永远等待
  9.set timeout 30 设置超时时间为30秒
  10.interact 将脚本的控制权交给用户,用户可继续输入命令   可以理解为结束标识
  11.expect eof 等待spawn进程结束后退出信号eof
  

1.安装expect
[root@test3 ~]# yum install expect -y
2.取消免密钥登录
  在对端的机器上删除/root/.ssh/authorized_keys

[root@test3 ~]# cat 1.exp 
#!/usr/bin/expect
spawn ssh root@192.168.23.102
expect {
    "yes|no" { send "yes\r"; exp_continue }  #ssh远程连接对端机器,如果出现yes或者no ,则自动的发送yes,send是发送嘛。r就是继续,为什么有exp_continue 很有可能有得时候是没有提示输入yes的,如果没有exp_contine 他就不往下走了,就卡在哪里了
    #	  怎么样出现yes? 删除对端机器的.ssh/know_hosts即可
    "password:" { send "centos\r" };  #遇到password字段就输入密码 send 发送密码
}
interact

此时就登陆上了
[root@test3 ~]# expect 1.exp 
spawn ssh root@192.168.23.102
root@192.168.23.102's password: 
Last login: Wed Jan 22 08:31:52 2025 from 192.168.23.1
[root@test2 ~]# 

使用expect免密传输文件
[root@master ~]# cat 1.exp 
spawn scp /root/1.py root@192.168.12.134:/root/
expect {
  "yes/no" { send "yes\r";exp_continue }
   "password" { send "1\r" };
}
interact
[root@master ~]# expect 1.exp 
spawn scp /root/1.py root@192.168.12.134:/root/
root@192.168.12.134's password: 
1.py                                                                                                                                                                            100%  269   284.0KB/s   00:0



进行多文件传输
[root@master ~]# cat 1.exp 
set file1 2.py
set file2 3.py
# 使用双引号进行变量替换
spawn scp /root/$file1 /root/$file2 root@192.168.12.134:/root/
expect {
  "yes/no" { send "yes\r"; exp_continue }
  "password" { send "1\r" }
}
interact

想传输不通目录下的
set file1 /home/user1/2.py
set file2 /var/log/3.py
set file3 /tmp/4.py
spawn scp $file1 $file2 $file3 root@192.168.12.134:/root/
expect {
  "yes/no" { send "yes\r"; exp_continue }
  "password" { send "1\r" }
}
interact

用循环的方法# 定义一个包含不同文件路径的列表
set files [list /home/user1/2.py /var/log/3.py /tmp/4.py]

# 遍历文件列表
foreach file $files {
    # 启动scp命令传输当前文件到远程主机
    spawn scp $file root@192.168.12.134:/root/
    expect {
        "yes/no" { send "yes\r"; exp_continue }
        "password" { send "1\r" }
    }
    # 等待scp命令执行完成
    expect eof
}
代码解释
定义文件列表:set files [list /home/user1/2.py /var/log/3.py /tmp/4.py] 这行代码定义了一个包含多个文件路径的列表 files,你可以按需添加或修改文件路径。
循环遍历文件列表:foreach file $files 用于遍历 files 列表中的每个文件路径。
传输文件:在每次循环中,使用 spawn scp $file root@192.168.12.134:/root/ 启动 scp 命令,将当前文件传输到远程主机的 /root/ 目录。
处理提示信息:和之前一样,使用 expect 语句块处理 yes/no 和 password 提示。
等待命令执行完成:expect eof 用于等待 scp 命令执行完毕,然后再处理下一个文件

正则表达式

过滤以m开头的行
[root@Shell ~]# grep"^m"test.txt
myblogishttp: blog.51cto.com
myqqnumis 572891887.

1.2.2过滤以m结尾的行
[root@Shell ~]# grep"m$"test.txt
myblogishttp: blog.51cto.com
oursiteishttp:www.lizhenya.com

1.2.3排除空行,并打印行号
[root@student~]#grep-vn"^$"lizhenya.txt

1.2.4匹配任意一个字符,不包括空行
[root@student~]#grep"."lizhenya.txt

1.2.5匹配所有
[root@student~]#grep".*"  lizhenya.txt

1.2.6匹配单个任意字符
[root@node1 ~]# grep"lizhen.a" lizhenya.txt

1.2.7以点结尾的
[root@student~]#grep"\.$"lizhenya.txt

1.2.8精确匹配到
[root@student~]#grep -o"8*"lizhenya.txt

1.2.9匹配有abc的行
[root@student~]#grep"[abc]"lizhenya.txt

1.2.10匹配数字所在的行"[0-9]"
[root@student~]#grep"[0-9]"lizhenya.txt

1.2.11匹配所有小写字母[a-z]
[root@student~]#grep"[a-z]"lizhenya.txt

1.2.12重复8三次
[root@student~]#grep"8\{3\}"lizhenya.txt

1.2.13重复数字8,3-5次
[root@student~]#grep-E"8{3,5}"test.txt
1.2.14至少1次或1次以上
[root@student~]#grep-E"8{1,}"lizhenya.txt

sed

sed是一个流编辑器,非交互式的编辑器,它一次处理一行内容.
处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(patternspace)

sed命令格式
sed[options]'command' file(s)
-e 允许多项编辑
-n 取消默认的输出
-i 直接修改对应文件
-r 支持扩展元字符
 
sed`内置命令参数
a 在当前行后添加一行或多行
c 在当前行进行替换修改
d 在当前行进行删除操作
i 在当前行之前插入文本
p 打印匹配的行或指定行
n 读入下一输入行,从下一条命令进行处理
! 对所选行以外的所有行应用命令
h 把模式空间里的内容重定向到暂存缓冲区
H 把模式空间里的内容追加到暂存缓冲区
g 取出暂存缓冲区的内容,将其复制到模式空间,覆盖该处原有内容
G 取出暂存缓冲区的内容,将其复制到模式空间,追加在原有内容后面


注释2-6行
 sed '2,6s/^/#/' /etc/passwd

awk

-F 定义输入字段分隔符,默认的分隔符,空格或者tab键
命令 command
行处理前     行处理  行处理后
BEGIN{}      {}     END{}

BEGIN发生在读文件之前
[root@test3 ~]# awk 'BEGIN{print 10 * 10}'
100
如果后面是文件的话,有几行就有几个100,awk是按行处理的
[root@test3 ~]# awk '{print 10 * 10}' 1.txt 
100
100
100


[root@test3 ~]# awk -F: '{print $1,$2}' 1.txt 
root x
bin x
daemon x
[root@test3 ~]# awk -F: 'BEGIN{OFS="--->"} {print $1,$2}' 1.txt 
root--->x
bin--->x
daemon--->x
[root@test3 ~]# awk -F: '{print $1"----"$2}' 1.txt
root----x
bin----x
daemon----x

想比对第三列大于0的行
[root@test3 ~]# cat 1.txt 
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@test3 ~]# awk -F: '$3>0' 1.txt 
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

取出用户,又的就对不起了
[root@test3 ~]#  awk -F: '{print "用户是:" $1 "\t 用户 uid: " $3 "\t 用户 gid:" $4}' /etc/passwd
用户是:root	 用户 uid: 0	 用户 gid:0
用户是:bin	 用户 uid: 1	 用户 gid:1
用户是:daemon	 用户 uid: 2	 用户 gid:2
用户是:adm	 用户 uid: 3	 用户 gid:4
用户是:lp	 用户 uid: 4	 用户 gid:7
用户是:sync	 用户 uid: 5	 用户 gid:0
用户是:shutdown	 用户 uid: 6	 用户 gid:0
用户是:halt	 用户 uid: 7	 用户 gid:0
用户是:mail	 用户 uid: 8	 用户 gid:12
用户是:operator	 用户 uid: 11	 用户 gid:0
用户是:games	 用户 uid: 12	 用户 gid:100
用户是:ftp	 用户 uid: 14	 用户 gid:50
用户是:nobody	 用户 uid: 99	 用户 gid:99
用户是:systemd-network	 用户 uid: 192	 用户 gid:192
用户是:dbus	 用户 uid: 81	 用户 gid:81
两种解决方案,一种是使用column -t
[root@test3 ~]#  awk -F: '{print "用户是:" $1 "\t 用户 uid: " $3 "\t 用户 gid:" $4}' /etc/passwd|column -t
用户是:root             用户  uid:  0      用户  gid:0
用户是:bin              用户  uid:  1      用户  gid:1
用户是:daemon           用户  uid:  2      用户  gid:2
用户是:adm              用户  uid:  3      用户  gid:4
用户是:lp               用户  uid:  4      用户  gid:7
用户是:sync             用户  uid:  5      用户  gid:0
用户是:shutdown         用户  uid:  6      用户  gid:0
用户是:halt             用户  uid:  7      用户  gid:0
用户是:mail             用户  uid:  8      用户  gid:12


还有一种是使用printf
printf 函数
如果觉得空格太小了,可以再给多一点字符
[root@test3 ~]# awk -F: '{printf "%-15s %-10s %-15s\n", $1, $2, $3}' /etc/passwd
root            x          0              
bin             x          1              
daemon          x          2              
adm             x          3              
lp              x          4              
sync            x          5              
shutdown        x          6              
halt            x          7              
mail            x          8              
operator        x          11             
games           x          12             
ftp             x          14             
nobody          x          99             
systemd-network x          192           
%s 字符类型
%d 数值类型
占 15 字符
- 表示左对齐,默认是右对齐
printf 默认不会在行尾自动换行,加\n

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值