shell是一门编程语言,如:C、C++、java、PHP、python Go等
语言分类
编程型语言:C、C++、Go等为编译型语言。程序运行需要提前编译,编译语言都有编译器
解释型语言:shell、PHP、python等为解释型语言,程序在运行时不需要提前编译,一边执行,一边解释,每种解释型语言都有解释器
shell语言支持大部分编程语言都具备的功能:if判断、for循环、函数、数组、加减乘除、逻辑运算等
脚本的本质就是命令的堆积
系统解释器
[root@xu script]# cat /etc/shells
/bin/sh #Unix解释器
/bin/bash #Linux默认解释器
/usr/bin/sh
/usr/bin/bash
这就是一个简单的shell script,在我们编写完成后,要给script加上可执行的权限
#!/bin/bash
#首先要进行环境指定,指定编译器,一般我们会使用/bin/bash,标准解释器
# '#'表示注释、‘#!’表示指定解释器
#功能:输出hello world
echo hello world
在脚本中不能出现交互式命令
#例如创建用户并且给用户设置密码
#!/bin/bash
#creat user
useradd zhanghaowei
echo 123 | passwd --stdin zhanghaowei
#我们使用设置密码的方式采用非交互式的方式
声明解释器(shebang)
注释
执行指令
自动配置本地yum仓库的脚本
[BaseOS]
name=local_BaseOS
baseurl=file:///mnt/cdrom/BaseOS
enable=1
gpgcheck=0
[AppStream]
name=local_AppStream
baseurl=file:///mnt/cdrom/AppStream
enable=1
gpgcheck=0
EOF
sleep 2
echo "======永久挂载光驱======"
echo "/dev/cdrom /mnt/cdrom iso9660 defaults 0 0" >> /etc/fstab
sleep 2
echo "======检查软件包数量======"
yum list | wc -l
方式一:赋予脚本执行权限后,可用绝对路径和相对路径执行
方式二:调用解释器执行脚本文件,会开启jie'shi'q
方式三:source和 . :执行脚本文件
#绝对路径执行脚本
[root@xu script]# /script/hello.sh
hello world
#相对路径执行脚本
[root@xu script]# ./hello.sh
hello world
#调用解释器执行脚本,调用解释器时可以不给执行权限
[root@xu script]# bash /script/hello.sh
hello world
父子shell问题
#我们知道脚本的执行是通过调用bash解释器去执行,我们通过sshd连接一个会话可以存在多个bash进程
─sshd(1065)───sshd(1896)───sshd(1900)───bash(1901)───bash(2093)───bash(2114)───pstree(2135)
#通过pstree我们可以看到当前session存在多个bash环境,都是由systemd进程创建出来的,而我们现在处于bash(2114)环境中,如果此时我们执行脚本采用的环境时bash(2114)环境。
#如果我们使用路径(source)去执行脚本和使用bash去执行脚本就会产生区别
#采用.(source)去执行脚本,是在当前的bash环境下执行的,而使用bash或path去执行脚本则是在当前的bash环境下创建出一个子bash去执行脚本,并切换当前环境为新的脚本
shell中变量类型只有string,没有别的;比方说在python中变量是有不同的类型的(list、dic、str。。。),但是在shell中只有str.
变量的存在让脚本变得更加灵活,能够应对不同的场景
语法:变量名="变量值"
$:调用变量
[root@xu ~]# name=$(hostname)
[root@xu ~]# echo $name
xu
# 系统自定义的系统变量
[root@moudle01 17:23:53 /script]# echo $PWD
/script
[root@moudle01 17:23:59 /script]# echo $SHELL
/bin/bash
[root@moudle01 17:24:05 /script]# echo $HOME
/root
[root@moudle01 17:24:14 /script]# echo $USER
root
[root@moudle01 17:24:51 /script]# echo $UID
0
在shell中也存在着和C语言相似的参数传递问题
只不过在shell中的参数问题是通过命令去完成的,我们在执行一个shell scrip时可以写上参数,将参数传递进shell script,同时在shell中去接收通过命令传入的参数。
位置参数:在执行脚本的命令中写上的参数就是位置参数,从1开始
在接收时可以使用以下标识符
- $0:提取文件名(用source执行脚本时,不显示文件名而是解释器名字)
- ${1}:提取位置参数为1的值
- ...
- ${20}: 提取位置参数为20的值
- $*:接收所有参数
- $@:接收所有参数
- $$:提取当前进程的PId
- $?:上一条命令的执行结果(上一个程序/命令的返回状态码)
- $#:所有参数个数
#!/bin/bash
echo $1
echo $2
echo $3
echo $0
echo $*
echo $#
echo $$
echo $?
[root@moudle01 21:10:40 /script]# bash test04.sh a b c d
a
b
c
test04.sh
a b c d
4
2728
0
[root@moudle01 21:18:33 /script]# kjasdhfsdlgasgasgadfg
-bash: kjasdhfsdlgasgasgadfg: command not found
[root@moudle01 21:18:45 /script]# echo $?
127
[root@moudle01 21:18:49 /script]# echo $?
0
创建用户tom,密码123
#!/bin/bash
useradd $1
echo $2 | passwd --stdin $1
env:所有环境变量
set:所有变量
[root@moudle01 21:30:10 /script]# abcd=6666666
[root@moudle01 21:30:55 /script]# set | grep abcd
abcd=6666666
[root@moudle01 21:31:00 /script]# env | grep PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
“”:双引号,引用整体,界定范围
‘’:单引号,引用整体,并且取消所有特殊字符含义,界定范围
[root@moudle01 21:32:13 /script]# a=" a"
[root@moudle01 21:36:17 /script]# echo "$a"
a
# 如果有特殊符号,定义时单引号和双引号都行,在引用时只能使用双引号
[root@moudle01 21:36:25 /script]# echo '$a'
$a
$[]、$(())、expr:四则运算
[root@xu script]# echo $[1+1]
2
[root@xu script]# echo $[2*3]
6
[root@xu script]# echo $[2-3]
-1
[root@xu script]# echo $[6/3]
2
[root@xu script]# echo $[10%3]
1
# expr 运算符号两侧必须加空格
[root@moudle01 16:13:56 ~]# expr 1 + 1
2
$():取命令结果作为参数
``:反撇号,将命令的输出结果作为参数
[root@moudle01 21:40:57 /script]# date
Wed Jun 12 21:41:07 CST 2024
[root@moudle01 21:38:26 /script]# a=`date`
[root@moudle01 21:40:54 /script]# echo $a
Wed Jun 12 21:40:54 CST 2024
[root@moudle01 21:41:07 /script]# x=$(ls)
[root@moudle01 21:42:37 /script]# echo $x
abc rsync_inotify.sh test01.sh test02.sh test03.sh test04.sh test05.sh
交互式输入变量
[root@moudle01 21:48:04 /script]# read -p "请输入:" a
请输入:123
[root@moudle01 21:48:47 /script]# echo $a
123
#!/bin/bash
read -p "请输入要创建的用户名:" a
useradd $a
read -p "请输入用户的密码:" p
echo $p | passwd --stdin $a
[root@moudle01 21:50:41 /script]# source test05.sh
请输入要创建的用户名:xxx
请输入用户的密码:123
Changing password for user xxx.
passwd: all authentication tokens updated successfully
在敲完之后,你在后面输入的所有内容都看不到
[root@moudle01 21:51:16 /script]# stty -echo
显示回显
[root@moudle01 21:51:16 /script]# stty echo
写入创建用户的脚本,让密码输入更加安全
#!/bin/bash
read -p "请输入要创建的用户名:" a
useradd $a
stty -echo
read -p "请输入用户的密码:" p
stty echo
echo ""
echo $p | passwd --stdin $a
[root@moudle01 21:59:24 /script]# source test05.sh
请输入要创建的用户名:aaa
请输入用户的密码:
Changing password for user aaa.
passwd: all authentication tokens updated successfully.
环境变量(全局变量):系统在全局生效的变量
普通变量(局部变量):只针对局部环境生效,如当前某一个login.bash脚本生效
全局变量和局部变量就是相对于父子shell(bash)。环境变量(全集变量)就是父shell也可以使用,普通变量(局部变量)只有子shell可以使用
变量生存周期
1、永久变量:写入文件,反复读取、加载,让其永久生效,如/etc/profile的PATH修改
2、临时变量:如命令行export定义的一个变量存储数据,关闭shell后失效
在定义变量时有两种选择
1、加export:在父shell中定义的变量可以在子shell中去使用,也就是全局变量
[root@xu ~]# export name="xuruizhao" [root@xu ~]# echo ${name} xuruizhao [root@xu ~]# bash Skill is acquired through repeated practice, and practice makes perfect. [root@xu ~]# bash Skill is acquired through repeated practice, and practice makes perfect. [root@xu ~]# echo ${name} xuruizhao
2、不加export:只对当前的shell有效,子shell看不到,也就是局部变量
[root@xu ~]# name="hello world" [root@xu ~]# echo ${name} hello world [root@xu ~]# bash #我们切换了bash环境,也就是进入了子shell,由于没有加export子shell中是不能使用父shell中定义的变量 Skill is acquired through repeated practice, and practice makes perfect. [root@xu ~]# echo ${name} [root@xu ~]#
使用let定义变量,可以在定义时进行计算
[root@moudle01 22:13:42 /script]# let a=1+1
[root@moudle01 22:14:21 /script]# echo $a
2
[root@moudle01 22:16:30 /script]# let a+=3
[root@moudle01 22:16:35 /script]# echo $a
5
上面的运算均不支持小数
bc命令支持小数
[root@moudle01 16:13:58 ~]# echo "1.1+1" | bc
2.1
#!/bin/bash
read -p "请输入用户名" username
if [ -z $username ];then
echo "用户名不能为空" && exit
fi
useradd $username
read -p "请输入密码" pass
echo ${pass:-123456} | passwd --stdin $username
[root@moudle01 11:37:41 /script]# bash user_add02.sh
请输入用户名
用户名不能为空
两种测试格式
test
[]
== 判断是否相同
! = 判断两侧是否不等
[root@moudle01 16:29:46 ~]# a=1
[root@moudle01 16:29:51 ~]# b=2
[root@moudle01 16:29:52 ~]# [ $a != $b ]
[root@moudle01 16:30:04 ~]# echo $?
0
如果一个变量值为空,我们在进行比较时,
1、可以加上双引号
2、-z:判断变量是否为空
! -Z/-n:判断变量是否非空
[root@moudle01 16:33:11 ~]# [ a == "$c" ]
[root@moudle01 16:33:23 ~]# echo $?
1
[root@moudle01 16:33:27 ~]# [ -z "$c" ]
[root@moudle01 16:35:13 ~]# echo $?
0
[root@moudle01 16:35:16 ~]# [ -z "$a" ]
[root@moudle01 16:35:22 ~]# echo $?
1
[root@moudle01 16:38:16 ~]# c=123
[root@moudle01 16:38:23 ~]# [ -n "$c" ]
[root@moudle01 16:38:26 ~]# echo $?
0
&&与
||或
#!/bin/bash
[ $USER == root ] || echo "非管理员禁止执行该脚本" && exit
yum -y install vsftpd > /dev/null
systemctl start vsftpd
systemctl enable vsftpd
# 只有root用户可以执行此脚本
#!/bin/bash
[ $USER != root ] && echo "非管理员禁止执行" && exit2
yum -y install vsftpd > /dev/null
systemctl start vsftpd
systemctl enable vsftpd
[root@moudle01 16:57:25 /script]# ls a || ls b && ls c || ls && ls /etc/yum.repos.d/
a
c
bak epel local_yum_house.repo
-eq:相等
-ne:不相等
-gt:大于
-ge:小于等于
-lt:小于
-le:小于等于
#!/bin/bash
[ $# -ne 2 ] && echo "输入变量数量错误" && exit
useradd $1
echo $2 | passwd --stdin $1
-e:判断文件是否存在,不关心文件类型
-f:判断文件是否存在,必须是普通文件
-d:判断文件是否存在,必须是目录
[root@moudle01 17:19:49 /script]# [ -e a ]
[root@moudle01 17:20:00 /script]# echo $?
0
[root@moudle01 17:20:03 /script]# [ -d a ]
[root@moudle01 17:20:09 /script]# echo $?
1
[root@moudle01 17:20:10 /script]# [ -f x ]
[root@moudle01 17:20:21 /script]# echo $?
1
-r:判断当前用户对文件是否有读权限(对root无效,只针对普通用户)
-w:判断当前用户对文件是否有写权限(对root无效,只针对普通用户)
-x:判断当前用户对文件是否有x权限
[root@moudle01 17:21:45 /script]# [ -r a ]
[root@moudle01 17:22:02 /script]# echo $?
0
[root@moudle01 17:22:05 /script]# [ -w a ]
[root@moudle01 17:22:09 /script]# echo $?
0
[root@moudle01 17:22:10 /script]# [ -x a ]
[root@moudle01 17:22:17 /script]# echo $?
1
每隔两分钟检查服务器的账户数量,如果发现增加,则给管理员发邮件
#!/bin/bash
num=$(cat /etc/passwd | wc -l)
[ num -gt 38 ] && echo "用户增加" | mail -s "user_info" root
# 编写计划任务
*/2 * * * * /script/test02.sh