shell
入门
解释性语言 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,\