shell

2

入门

解释性语言  shell  python

编译型语言 go java

shell:

#!/bin/bash

echo “nihao”

变量:

name=‘nihao’

echo $name

name=$(zhaoxi)

echo $name

父子进程:

![[Pasted image 20240515095354.png]]
定义变量:

单引号:所见即所得   time=zhaoxi

双引号:有特殊符号 或定义别的变量   time=“你好 你好” time=${time} 

反引号:linux命令  time=date +%F-%T  同义词 $(date +%F-%T)  变量是{} 直接结果是()

打印变量:

echo $name

echo ${name} 

区别  echo $name666 没有边界

修改:

name=赵析

直接name=我是朝夕

删除:set 查看当前变量 配合grep查询定义的变量

set | grep ^name

unset name2 name3

参数传递:

${1} 提取第一个参数 1-9可以直接$num

$# 判断参数个数

$$ 提取当前进程pid    

$? 判断上条命令是否正确 返回0正确 其他错误

$* 接受所有位置参数

$@ 接受所有位置参数 (区别下面有例子)

$0 显示当前运行的脚本名称

脚本:

1.创建用户并设置密码
![[Pasted image 20240515095634.png]]
2.无交互式修改主机名
![[Pasted image 20240515095710.png]]

read 把输入的变成脚本中的变量
read -s 默认不限时输入内容 比如密码
![[Pasted image 20240515095725.png]]
3.拷贝目录
echo -e 显示转义字符
![[Pasted image 20240515095741.png]]
4.根据输入信息创建用户密码 并将信息按照username:pwd的格式保存到/tmp/test.log
![[Pasted image 20240515095757.png]]

read 交互式

位置参数 非交互式

sed -i ‘/ipadress/c ipadress’ filename    

-i 替换  /字母/ 匹配   c删除这一行  后面跟新字母  目录

5.更改hostname和ip
![[Pasted image 20240515095842.png]]
6.cron脚本
![[Pasted image 20240515095854.png]]
7.探测多少机器存活
![[Pasted image 20240515095904.png]]
8.$@和$*的区别 (for循环)
![[Pasted image 20240515100009.png]]
eg2: 批量创建用户
![[Pasted image 20240515100023.png]]

  9.日期表达

expr 3 + 2

expr 3 \* 2

现在是$(date +%Y-%m-%d)

去年是$[$(date +%Y)-1]年

"现在是$(date +%Y-%m-%d-%T)"

现在是2024-04-01-23:05:03

  

echo "今年还剩下$[(365-10#$(date +%j))/7]周"

今年还剩下39周

10 代表后面的数值按10进制计算

计算器1

![[Pasted image 20240515100138.png]]
查看系统信息:
![[Pasted image 20240515100157.png]]
![[Pasted image 20240515100201.png]]
查看内核版本
![[Pasted image 20240515100206.png]]
查看网卡IP:
![[Pasted image 20240515100230.png]]
查看内存使用:
![[Pasted image 20240515100241.png]]
查看磁盘使用:
![[Pasted image 20240515100251.png]]
查看cpu使用:
![[Pasted image 20240515100301.png]]
查看公网ip:
![[Pasted image 20240515100311.png]]

final:

![[Pasted image 20240515100325.png]]

条件判断

基于文件判断

1.
if [  -f /root/ip ];then
        echo "有这个文件"
fi

2.
[  -f /root/ip ] && echo "yes"
3.(use test -f)
test -f /root/ip && echo "yes"
(&& 前面成立后面才会执行)
(|| 前面不成立后面才会执行)
(可以看系统脚本 写的都很好)

基于整数判断

-eq 等于
-ne 不等于
-gt 大于
-lt 小于
-ge 大于等于
-le 小于等于

#返回码0是正确,非0是报错

猜数字脚本 guess_num.sh
#!/bin/bash
result=100
read -p "please input your num :" num
if [ $num -gt $result ];then
        echo "too big"
elif [ $num -lt $result ];then
        echo "too small"
else echo "nb"
fi
美化结果
#!/bin/bash

. /etc/init.d/functions

result=100
read -p "please input your num :" num
if [ $num -gt $result ];then
        action "too big" /bin/false
elif [ $num -lt $result ];then
        action "too small" /bin/false
else action "nb" /bin/true
fi

# 引用系统自带的functions库 把echo换成action 后面跟上/bin/false or true
改进
#!/bin/bash

. /etc/init.d/functions

result=100
read -p "please input your num :" num
if [ -z "$(echo $num | sed -rn '/^[0-9]+$/p')" ];then
        echo "please input num" && exit
fi
#通过sed 正则取出不是空的字符 如果没有数字就exit
if [ $num -gt $result ];then
        action "too big" /bin/false
elif [ $num -lt $result ];then
        action "too small" /bin/false
else action "nb" /bin/true
fi
统计个数
pass=$(echo $1 | wc -L)
[ $pass -ne 6 ] && echo "不是6个" && exit
echo "yes"

test=$1
pass=$(echo "${#test}")
[ $pass -ne 6 ] && echo "不是6个" && exit
echo "yes"

两种用法

基于字符串判断


登陆脚本:
username=zhaoxi
passwd=mima
read -p "please input your name:" name
read -p "please input your passwd:" pass
[ $username == $name -a $passwd == $pass ] && echo "yes" || echo "no"
  • -eq 和 -ne 是用于 整数 比较的,== 和 != 是用于 字符串 比较的。
判断服务是否运行:
if [ $# -eq 1 ];then
        systemctl status $1 &>/dev/null    
        if [ $? -eq 0 ];then
                echo "action"
        else
                echo "stop"
        fi
else
        echo "please input 1 service_name"
        exit 1
fi
  • 重定向&>/dev/null 必须要一个& 不写只会重定向正确的 错误的不会 &&是逻辑与运算 如果前面为假就不会重定向
  • 如果要不显示标准输出>/dev/null
  • 如果要不显示标准输出和标准错误&>/dev/null
  • 和 >> 没啥区别 后者只是追加写入

内存告警:
#!/bin/bash
now=$(date +%F-%T)
mem_used=$(free -m | awk '/Mem/{used=$3/$2;print used*100}' | sed -r 's/\..*//')
if [ $mem_used -ge 10 ];then
        echo "${USER}:${now}:mem is ${mem_used}%" >> /root/shell/mem.log
fi
  • $USER必须要大写

case判断

简易atm机
#!/bin/bash
echo -e "----------------------------
1.取钱
2.存钱
3.查余额
----------------------------"
read -p "请输入您的选择:" num
case "$num" in
1)
        echo "您取走了10块"
        ;;
2)
        echo "您存了20块"
        ;;
3)
        echo "您的余额是30块"
        ;;
*)
        echo "输错了"
esac
  • 一个结果执行完要;; 最后要esac 跟if fi一样
case正则
  • case支持正则 但是不多
1. * 表示任意字符
2.  ? 表示单个字符
3.  [abc] 表示abc中任一字符 一个
4.  [m-n] 表示m-n中任一字符  一个 [0-9] [a-z] [A-Z]
5.  | 多重选择  比如 start | stop
nginx日志分析
#!/bin/bash
echo -e "---------------------日志分析系统
1.显示当前机器信息
2.查询pv,uv
3.显示访问量最高的10个ip
4.显示访问最频繁的10个业务url
5.显示各种搜索引擎爬虫访问本站的次数
6.显示都有哪些客户端访问了本网站"

case $1 in
1)

......

for循环

简单例子
for i in {1..50};do echo "i am $i";done
for i in $(echo $PATH | sed 's/:/ /g');do echo $i;done
for与sed
new_path=$(echo $PATH | sed 's/:/ /g')
for i in $new_path
do
        echo $i
done
for与awk
for的c语言写法
#!/bin/bash
for (( i=0;i<15;i++ ))
do
        echo $i
done
for遍历文件执行命令
cat >user_info.log<<EOF
> user1:pass1
> user2:pass3
> user3:pass4
> user4:pass2
> EOF
#!/bin/bash
for i in $(cat user_info.log)
do
        user=$(echo $i | awk -F: '{print $1}')
        pass=$(echo $i | awk -F: '{print $2}')
        useradd $user
        echo $pass | passwd --stdin $user >>/dev/null && echo "succesfull~"
#       userdel -r user
done
单进程主机存活检测
#!/bin/bash
for i in {1..254}
do
        ping -w 1 192.168.32.$i &>/dev/null
        if [ $? == 0 ];then
        echo "192.168.32.$i 正在存活中" >> ./online.log
        fi
done
  • &>/dev/null 和 >/dev/null 2>&1 效果一样 都是把所有输出扔到黑洞
  • for就这样吧 没啥意思 多练

while循环

  • 使用场景:
    需要死循环时;因为for虽然可以定范围 for i in [1..10000] 但是也会有结束的时候,while可以做死循环 比如系统资源监控脚本
  • 这里先不学了 加紧学function

循环控制

break

  • 跳出本个循环
#!/bin/bash

for i in {1..20}

do

        if [ $i == 11 ];then

              break;

        fi

        echo "这是第${i}次"

done

continue

  • 跳过本次循环

exit

  • 直接退出当前进程-脚本直接结束

函数

  • 用于封装代码块,复用代码;先定义再调用
  • 格式:函数名(){
    函数体
    }
#!/bin/bash
function hell(){
        echo "hello world"
}

function docker_status(){
systemctl status docker
}

hell
docker_status
  • function可以省略

函数传参

[root@tx_yun ~/shell/function_study] cat 1.sh 
#!/bin/bash
name=zhaoxi
function echo_name(){
        echo "output $name"
}

function hello(){
        echo "this is first !"
}
hello
[root@tx_yun ~/shell/function_study] cat 2.sh 
#!/bin/bash
. 1.sh
echo_name
#这时候运行2的结果
this is first !
output zhaoxi
  • . *.sh 导入函数 会把函数里面的动作执行 也可以在下面调用这个脚本定义的函数

函数内参数与脚本外参数

#!/bin/bash
function canshu(){
        echo "di yi ge can shu : " $1
        echo "di yi ge can shu : " $2
}
canshu 1 2 
echo "脚本外的参数3" $1
echo "脚本外的参数4" $2

[root@tx_yun ~/shell/function_study] bash 3.sh 3 4
di yi ge can shu :  1
di yi ge can shu :  2
脚本外的参数3 3
脚本外的参数4 4
  • $0 脚本文件名

计算器

  • 函数参数调用脚本参数
function calc(){
        case $2 in
        +)
                echo "$1+$3=$(( $1+$3 ))"
                ;;
        -)
                echo "$1-$3=$(( $1-$3 ))"
                ;;
        x)
                echo "$1*$3=$($1*$3)"
                ;;
        /)
                echo "$1/$3=$($1/$3)" 
                ;;
        *)
                echo "budui"
        esac
}

calc $1 $2 $3
  • 有很多细节 x不用* 因为* 要留着接受其他变量
  • 最后必须calc 1 2 $3 因为上面只是定义 并没有执行东西

系统自带脚本学习

/etc/init.d/netconsole
usage (){
        echo $"Usage: $0 {start|stop|status|restart|condrestart}" 1>&2
}
...
restart ()
{
        stop
        start
}
#函数调用函数
...
case "$1" in
    stop) stop ;;
    status) status ;;
    start|restart|reload|force-reload) restart ;;
    condrestart) condrestart ;;
    *) usage ;;
esac

多级菜单

#!/bin/bash
# author: www.yuchaoit.cn


# 菜单封装函数,便于调用

menu1(){

echo -e "
=============欢迎来到于超老师的linux课程========
兄弟,请你按照如下规则,输入选项
1. Install Nginx
2. Install Mysql
3. Install Redis
4. bye
============================================
"
}


nginx_menu(){
echo -e "
===============请选择对于软件版本==============
1. Install Nginx1.15
2. Install Nginx1.16
3. Install Nginx1.17
4. 返回上一层
=============================================
"
}


mysql_menu(){
    echo "mysql菜单还在开发中....."
}


# 程序打印菜单1
menu1

while true
do
    # 用户选择菜单1
    read -p "您请输入对应的序号:" num1
    # 一级条件判断
    case $num1 in

    1)
        # 进入菜单2
        nginx_menu
        while true
        do
            read -p "请选择对应的nginx版本:" num2
        case $num2 in
            1)
              echo "成功安装nginx 1.15版本!!" 
              ;;
            2)
              echo "成功安装nginx 1.16版本!!" 
              ;;
            3)
              echo "成功安装nginx 1.7版本!!"
              ;;
            4)
              # 清屏,返回上一层
              clear
              menu1
              # 中断二级菜单的循环
              break
              ;;
             *)
              echo "请按规则填写 1 ~ 4 序号...."
         esac
            done
            ;;
    2)
        mysql_menu
        ;;
    3)
        echo "redis菜单于超老师努力开发中。。。"
        ;;
    4)
        echo "bye bye 。"
        exit
        ;;
    *)
        echo "请按规则输入菜单序号1 ~ 4 !!"
        continue
    esac
done
  • 自己好好看看

自己开发脚本

  • 要求:登录成功显示家目录菜单 失败三次提示
    ![[Pasted image 20241105220819.png]]
  • 自己看能不能开发多级菜单那个填充好
2.1  服务管理脚本开发,基于课上讲解的多级菜单模板,继续优化开发
(while循环的嵌套,case语句的嵌套)


能够实现
1. 执行该脚本,安装nginx1.16 ,安装全部再后台执行,不显示日志
2. 输入回退到上一级选项
3. 选择安装mysql的序号,简单调用yum安装mysql即可
4. 输入退出选项,结束程序

shell结束🎉

自动安装脚本

  • function
#!/bin/bash
# ower:zhaoxi@zhizhangyi.com
# 判断是否传入参数
function check_usage(){
        if [ -z "$1" ]; then
                echo "usage: $0 {mbs|sdp}"
                exit 1
        fi
}
#判断哪个用户版本
function check_user(){
        case $1 in
        mbs)
                user_name=mbs
                user_id=2001
                THIRD_DIR=/opt
                echo "V5.0.0~V5.2.1 回复1"
                echo "V5.2.2~V5.3.0 回复2"
                echo "V5.3.1+       回复3"
                read -p "Please enter your choice: " version
        ;;
        sdp)
                user_name=sdp
                user_id=2002
                THIRD_DIR=/opt
                PRODUCT_SDP_DIR=/home/sdp/usdp
                echo "V5.0.0~V5.2.4 回复1"
                echo "V5.3.0        回复2"
                echo "V5.3.1+       回复3"
                read -p "Please enter your choice: " version
        ;;
        *)
                echo "usage : $0 {mbs|sdp}"
                exit 1
        esac
}
# 判断操作系统
function check_os(){
        os_name=$(awk -F'=' '/^ID=/{print $2}' /etc/os-release)
        echo "当前操作系统为$os_name"
        if [[ "$os_name" == "kylin" || "$os_name" == "uos" || "$os_name" == "openEuler" ]]; then
                sudo echo "$1" >> /etc/cron.allow
        fi
}
# 定义创建用户函数
function create_user(){
        groupadd -g ${user_id} ${user_name} &>/dev/null
        useradd -u ${user_id} -g ${user_name} -m -s /bin/bash ${user_name} &>/dev/null
        ssh_pwd=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-12};echo;)
        echo ${ssh_pwd} > pass.txt
        echo ${ssh_pwd} | passwd --stdin ${user_name} &>/dev/null
        }
# 定义免密
function mbs_ssh(){
        case $version in
        1)
                cmd_str="/usr/sbin/iptables,/usr/sbin/gluster,/usr/sbin/exportfs,/usr/sbin/showmount,/usr/bin/mount,/usr/bin/su,\
/usr/bin/umount,/usr/bin/systemctl,/usr/bin/sed,/usr/bin/tee,/usr/bin/vi,/usr/bin/mkdir,/usr/bin/cp,\
/usr/bin/rm,/usr/bin/chown,/usr/bin/chmod,/usr/bin/ln,/usr/bin/hostnamectl,/usr/bin/rpm,/usr/bin/yum,\
/usr/bin/pkill,/usr/bin/grep,/usr/sbin/setenforce,/usr/sbin/sysctl,/usr/bin/mv,/usr/bin/tar,/usr/bin/pip3,\
${THIRD_DIR}/zzyoss/zc,/usr/bin/java,/usr/bin/dpkg"
                sed -i "/^root.*ALL=(ALL.*).*ALL/a ${user_name}\tALL=(ALL)\tNOPASSWD: ${cmd_str}\t" /etc/sudoers
        ;;
        2)
                cmd_str="/usr/sbin/iptables,/usr/sbin/gluster,/usr/sbin/exportfs,/usr/sbin/showmount,/usr/bin/mount,\
/usr/bin/umount,/usr/bin/systemctl,/usr/bin/sed,/usr/bin/tee,/usr/bin/vi,/usr/bin/mkdir,/usr/bin/cp,\
/usr/bin/rm,/usr/bin/chown,/usr/bin/chmod,/usr/bin/ln,/usr/bin/hostnamectl,/usr/bin/rpm,/usr/bin/yum,\
/usr/bin/pkill,/usr/bin/grep,/usr/sbin/setenforce,/usr/sbin/sysctl,/usr/bin/mv,/usr/bin/tar,/usr/bin/pip3,\
${THIRD_DIR}/zzyoss/zc,/usr/bin/java,/usr/bin/dpkg"
                sed -i "/^root.*ALL=(ALL.*).*ALL/a ${user_name}\tALL=(ALL)\tNOPASSWD: ${cmd_str}\t" /etc/sudoers
        ;;
        3)
                cmd_str="/usr/sbin/iptables,/usr/sbin/exportfs,/usr/sbin/showmount,/usr/bin/mount,\
/usr/bin/umount,/usr/bin/systemctl,/usr/bin/sed,/usr/bin/tee,/usr/bin/vi,/usr/bin/mkdir,/usr/bin/cp,\
/usr/bin/rm,/usr/bin/chown,/usr/bin/chmod,/usr/bin/ln,/usr/bin/hostnamectl,/usr/bin/rpm,/usr/bin/yum,\
/usr/bin/pkill,/usr/bin/grep,/usr/sbin/setenforce,/usr/sbin/sysctl,/usr/bin/mv,/usr/bin/tar,\
${THIRD_DIR}/zzyoss/zc,${THIRD_DIR}/ntp/ntpdate,${THIRD_DIR}/ntp/ntpd,${THIRD_DIR}/ntp/ntpq,/usr/bin/java,/usr/bin/dpkg,/usr/sbin/ldconfig,/usr/bin/timedatectl"
                sed -i "/^root.*ALL=(ALL.*).*ALL/a ${user_name}\tALL=(ALL)\tNOPASSWD: ${cmd_str}\t" /etc/sudoers
        ;;
        *)
                echo "usage : 参数不对"
        esac
}
function sdp_ssh(){
        case $version in
        1)
                sed -i "/^root.*ALL=(ALL.*).*ALL/a ${user_name}\tALL=(ALL)\tNOPASSWD: ALL\t" /etc/sudoers
                sed -i "s%IS_CHECK_SUDO=1%IS_CHECK_SUDO=0%" config/zzyenv.ini
        ;;
        2)
                cmd_str="/usr/sbin/iptables,/usr/bin/systemctl,/usr/bin/sed,/usr/bin/tee,/usr/bin/vi,/usr/bin/mkdir,\
/usr/bin/rm,/usr/bin/chown,/usr/bin/chmod,/usr/bin/ln,/usr/bin/hostnamectl,/usr/bin/rpm,/usr/bin/yum,\
/usr/bin/pkill,/usr/bin/grep,/usr/sbin/setenforce,/usr/sbin/sysctl,/usr/bin/mv,/usr/bin/tar,/usr/bin/su,\
/usr/bin/pip3,/usr/bin/kill,/usr/bin/touch,/usr/sbin/lsmod,/usr/sbin/ip,/usr/sbin/rmmod,/usr/bin/cp,\
/sbin/modprobe,/usr/bin/chgrp,/usr/sbin/ip6tables,/usr/sbin/ipset,/usr/local/openresty/nginx/sbin/nginx,\
/usr/bin/java,/usr/bin/dpkg,/usr/bin/more,${THIRD_DIR}/unbound/sbin/unbound-control,/usr/sbin/ip6tables-restore,\
/usr/bin/echo,/usr/sbin/ldconfig,/usr/bin/lscpu,/usr/bin/cat,/usr/bin/md5sum,/usr/sbin/virt-what,\
/usr/bin/timedatectl,/usr/sbin/insmod,/usr/sbin/iptables-restore,/usr/bin/ps,/usr/local/guacamole/sbin/guacd,\