1、shell 概述
shell 是一个命令行解释器,接收应用程序、用户的命令,然后调用操作系统的内核,shell可以当成是一个功能强大的编程语言,易编写、易调试、灵活性强。
几种常见的shell
sh、 bash、chs等等,是由不同的组织机构开发的不同的 Shell,它们各有所长,有的占用资源少,有的支持高级编程功能,有的兼容性好,有的重视用户体验;
echo $SHELL
// 查看系统默认的shell
cat /etc/shells
//当前Linux系统可用的shell都记录在 /etc/shells文件中
ls -l /bin/sh
// 在现代的 Linux 上,sh 已经被 bash 代替,/bin/sh往往是指向/bin/bash的符号链接。
2、写一个hellworld.sh文件
创建文件夹,并创建一个shell文件
mkdir test
cd test
vim hello.sh
以下是hello.sh文件内容
###################
#!/bin/bash -->指定解析器
echo "hello world"
保存后运行文件,当前文件夹下面可直接运行
bash hello.sh -->本质是bash解析器帮你执行脚本,所有不需要权限
先给hello.sh赋权限,可用省略bash
chomd +x hello.sh
hello.sh --> 本质是脚本自己需要执行,因此需要执行权限
3、变量
3.1系统预定义变量
常见的系统变量
$HOME $PWD 、$SHELL 、$USER 等等
使用env命令,可用直接查看当前系统所有环境变量
env
查看系统变量的值
echo $HOME
显示当前shell中的所有变量
set
3.2自定义变量
1、基本语法
定义变量: 变量名=变量值,注意等号前后不能有空格
my_test=hello
修改变量: 变量名=变量值, 直接修改变量值即可
my_test=Hello
父shell的值可以让子shell读到,提升为全局变量
export my_test
子shell此时可以读到my_test,也可以修改my_test,但是子shell中的修改不会影响到父shell中的值
看父子的继承关系
ps -f
定义只读变量
readonly a=5 -- 不能unset 只读变量
撤销变量: unset 变量名
3.3特殊变量
基本语法
$n
n为数字,$0代表该脚本名称,$1~$9代表第一个到第九个参数,超过10的参数,需要用大括号包含,如${10}
echo "hello $1" xiaoming
--> hello xiaoming
$#
获取所有输入参数个数,常用于循环,判断参数的个数是否正确以及加强脚本的健壮性
$*
代表命令行中所有的参数,$*把所有的参数看成一个整体
$@
代表命令行中所有的参数,$@把每个参数区分对待
$?
最后一次执行的命令的返回状态,如果遍历的值为0,证明上一个命令正确执行,
如果非0(具体是哪个数,由命令自己决定),则证明上一个命令执行不正确了
4、运算符
基本语法
$((运算式)) 或者 $[运算式]
计算2+3的值
$((2+3))
$[2+3]
expr 2 + 3 //加号左右两边要有空格,计算结果5
expr 2+3 //加号左右两边没有空格,结果是字符串 2+3
5、条件判断
基本语法
1、test condition // test后面跟条件表达式
a=hello //定义a=hello,等号前后不能有空格
test $a = hello // 测试a是否等于hello,注意等号前后要有空格
echo $? // 输出0,说明上面的结果是对的
2、[ condition ] // 中括号里面加条件表达式,注意condition前后都要有空格
[ $a = hello ] // 测试a是否等于hello
echo $? // 输出0,说明上面的结果是对的
注意,条件非空则返回true 如[ aaa ],条件为空返回false 如[ ]
常用条件判断
1、两个整数之间比较
-eq 等于
-ne 不等于
-lt 小于 less than
-gt 大于 greater than
-le 小于等于 less equal
-ge 大于等于 greater equal
如果是字符串之间的比较,用等号 = 判断相等,用 != 判断不等
2、按照文件权限进行判断
-r 有读的权限 read [ -r hello.sh ]
-w 有写的权限 write [ -w hello.sh ]
-x 有执行的权限 execute [ -x hello.sh ]
3、按照文件类型进行判断
-e 文件存在
-f 文件存在并且是一个常规的文件file
-d 文件存在并且是一个目录directory
4、多条件判断
逻辑与 &&
逻辑或 ||
-a 逻辑与
-o 逻辑或
if [ $a -gt 18] && [ $a -lt 35]; then echo ok; fi
if [ $a -gt 18 -a $a -lt 35]; then echo ok; fi
可以实现类似三元表达式的效果
[ condition ] && echo ok || echo notok
6、流程控制
1、if判断
基本语法
##########################
单分支
if [ condition ];then
程序
fi
或者
if [ condition ]
then
程序
fi
例子
if [ $a -gt 18 ] && [ $a -lt 35 ]; then echo ok; fi
if [ $a -gt 18 -a $a -lt 35 ]; then echo ok; fi
##############################################
多分支判断1
if [ condition ]
then
xxx
eles
yyy
fi
例子
if [ $a -le 18 ]
then
echo "未成年"
else
echo "成年人"
fi
###############################################
多分支判断2
if [ condition ]
then
xxx
elif [ condition ]
then (#跟在if后面就要加个then)
yyy
eles
zzz
fi
例子
if [ $a -le 18 ]
then
echo "未成年"
elif [ $a -ge 60 ]
then
echo "老年人"
else
echo "成年人"
fi
2、case语句
基本语法
case $变量名 in #case 行位必须为in
"值1") #值1可以加引号,也可以不加,匹配必须以)作为结束
#如果变量的值等于 值1,则执行此段程序
;; #双分号标识命令序列结束,相当于break
"值2")
#如果变量的值等于 值2,则执行此段程序
;;
*) #表示默认模式,相当于default
#如果变量的值和上述都不匹配,则执行此段程序
;;
esac
例子
case $a in
1)
echo "one"
;;
2)
echo "two"
;;
3)
echo "three"
;;
*)
echo "other"
;;
esac
3、 for 循环
基本语法1:
for (( 初始值;循环控制条件;变量变化 ))
do
xxxx #程序
done
例子:从1加到100
sum=0
for (( i=1;i<=100;i++ )) #因为用到了双小括号,里面可以直接用数学计算式,但是建议还是用[]
do
sum=$[ $sum + $i ] #取值的时候,前面要加上$
done
echo $sum
基本语法2
for 变量 in 值1 值2 值3 ...
do
程序
done
例子
循环打印
for os in linux windows macos
do
echo $os
done
计算1~100的和
for i in {1..100}
do
sum=$[ $sum + $i ]
done
echo $sum
#测试$* $@,将下列写在脚本文件里
#!bin/bash
echo '========$*=========='
for param in $*
do
echo $param
done
echo '========$@=========='
for param in $@
do
echo $param
done
# 执行上述脚本文件,后面传入参数为 a b c d e
# 打印的结果是相同的,是分别将参数打印出来
原因 : $* $@在脚本文件中不加引号,效果是相同的
#!bin/bash
echo '========$*=========='
for param in "$*"
do
echo $param
done
echo '========$@=========='
for param in "$@"
do
echo $param
done
# 执行上述脚本文件,后面传入参数为 a b c d e
# 打印的结果是不同的
# "$*" 是将传入的参数当作一个参数打印
# "$@" 是将传入的参数按照空格分割打印
原因 : $* $@在脚本文件中加引号,效果不相同
4、 while循环
while [ 条件表达式 ]
do
程序
done
例子,1加到100
i=1
sum=0
while [ i -le 100 ]
do
sum=$[ $sum + $i ] # let sum+=i
i=$[ $i + 1 ] # let i++
done
echo sum
7、read 读取控制台输入
基本语法
read (选项) (参数)
选项:
-p: 指定读取值时的提示符号
-t: 指定读取值时等待的时间(秒),如果不加则一直等待
参数
变量:指定读取值的变量名
例子:7s内,读取控制台输入的名称
#!/bin/bash
read -t 7 -p "请输入你的名字" name
echo "welcome $name"
8、函数
1、系统函数
1.1 basename
基本语法
basename 字符串 后缀 #就是取最后一个/后面的内容,如果同时传入了后缀,则会把后缀也去掉
basename /root/aaa/bbb/ccc.txt
# ccc.tx
basename /root/aaa/bbb/ccc.txt .txt
# ccc
1.2 dirname
基本语法
dirname 文件绝对路径 (从给定的包含绝对路径的文件名中去除文件名,即非目录部分,
然后返回剩下的目录的部分)
dirname /root/aaa/bbb/ccc.txt
# /root/aaa/bbb
2、自定义函数
基本语法 #带[]表示可以省略
[ function ] functionName[()]
{
Action;
[return i;]
}
注意事项:
1、必须在函数调用之前先声明函数,shell脚本时逐行运行,不会像其他语言一样先编译
2、函数返回值只能通过$?系统变量获得,可以显示的加 return 返回,
如果不加,则以最后一条命令运行结果作为返回值,return后面跟的数值 0~255
例子,计算两数字之和
function add()
{
s=$[$1 + $2] #位置参数
return $s
}
read -p "请输入第一个参数" a
read -p "请输入第二个参数" b
add $a $b #调用函数
echo $?
# 这样写会有一些问题:即return只能返回0~255的数值,
# 如果计算结果超出这个范围,虽然不会报错,单数结果不准确
改写:
function add()
{
s=$[$1 + $2] #位置参数
echo $s # 返回计算结果改为输出计算结果
}
read -p "请输入第一个参数" a
read -p "请输入第二个参数" b
sum=$(add $a $b)
#调用函数,没有return,则以最后一条命令作为返回值,
#将这个返回值赋值给sum ,然后输出sum
echo $sum
9、正则表达式
1、^ 匹配行首
cat /ect/password | grep ^a #匹配每行以a开头的行
2、$ 匹配行尾
cat /ect/password | grep bash$ #匹配每行以bash结尾的行
^$ 匹配的是空行
^xxx$ 匹配的是以xxx为内容的行
3、. 匹配的是任意一个字符
cat /ect/password | grep r..t #匹配包含 r..t的行,可以是root、r/ht等等
4、* 不单独使用,和上一个字符连用,表示匹配上一个字符0次或者多次
cat /ect/password | grep ro*t #匹配 rt rot root rooot等
.*表示任意一个字符出现任意次,就说明匹配到的是任意字符,也可以是空
^a.*bash$ #表示每行以a开头,bash结尾的任意行
^a.*var.*bash$ #表示每行以a开头,bash结尾,中间带var的任意行
5、字符区间,中括号 []
[6,8] 匹配6和8
[0-9] 匹配0到9
[0-9]* 匹配任意长度的数字串
[a-z] 匹配小写字母
[a-z]* 匹配任意长度小写字母字符串
6、\ 表示转义
10、文本处理工具
1、 cut
负责剪切数据用的,cut命令从文件的每一行剪切字节、字符和字段并输出
基本用法
cut [选项参数] filename
说明 :默认分隔符是制表符
选项参数说明:
-f 列号,提取第几列
-d 分隔符,按照指定分隔符分割列,默认制表符“\t”
-c 按照字符进行切割,后面加n标识取第几列 比如 -c 1
定义 文件 test.txt
======================
aaa aaa
bbb bbb
ccc ccc
ddd ddd
======================
cut -d " " -f 1 text.txt #按照空格分割,截取第一列
cut -d " " -f 2,3 text.txt #按照空格分割,截取第二,三列
cat /etc/password | grep bash$ | cut -d ":" -f 1,6,7
# cat 查看password文件,后面跟管道符查询以bash结尾的行
# 然后跟管道符 使用cut,表示对前面过滤到的信息进行裁剪
# 以:为分隔符,取1.6.7列 ------取出来的值还是用原来的分隔符进行拼接
cat /etc/password | grep bash$ | cut -d ":" -f 1,6-
# 取第一列,第6列以以后所有的
cat /etc/password | grep bash$ | cut -d ":" -f -4
# 取包括第4列及之前的
ifconfig ens33 | grep netmask | cut -d " " if 10
2、 awk
强大的文本分析工具,把文件逐行读入,以空格默认分割符将每行切片,切开的部分在分开处理
基本用法
awk [选项参数] '/pattern1/{action1} /pattern2/{action2} ...' filename
pattern: 表示awk在数据中查找的内容,就是匹配模式,正则,没有正则就可以省略
action: 在找匹配内容时所执行的一系列命令
选项参数
-F 制定文件分隔符,默认空格
-v 赋值一个用户定义变量
例子:
1、搜素password文件中,以root开头的所有行,并输出该行的第1列和第7列,中间以“,”分割
cat /etc/password |grep ^root | cut -d ":" -f 7
cat /etc/password | awk -F ":" '/^root/{print $7}'
cat /etc/password | awk -F ":" '/^root/{print $1","$7}'
#双引号包裹 , 做字符串拼接
#如果没有双引号包裹 , 是默认空格分割
#如果$1$7 或者$1 $7 是直接拼接在一起,没有空格分割
2、在password文件中,输出第1列和第7列,中间以“,”分割,并在所有行之前添加“user,shell”,
所有行之后加“end,byebye”
cat /etc/password | awk -F ":" 'BEGIN{print "user,shell"} {print $1","$7} END{print "end,byebye"}'
BEGIN : 在所有数据读取行之前执行
END : 在所有数据执行之后执行
3、将password文件中用户的id(第3列)数值增加1并 输出
cat /ect/password | awk -F ":" '{print $3+1}'
cat /ect/password | awk -v i=1 -F ":" '{print $3+i}' #用变量
4、awk的内置变量
FILENAME 文件名
NR 已读的记录数(行号)
NF 切割后,列的个数
输出password文件中每行的列数
awk -F ":" '{print "filename:"FILENAME "行数:"NR "列数:"NF}' /ect/password
输出ifconfig中的空行
ifconfig | grep -n ^$ # -n是输出行号,但是默认的行号后面带冒号:,无法更改
ifconfig | awk '/^$/ {print NR}' # 这样就没有冒号了
输出ipconfig中的ip
ifconfig | grep netmask | cut -d " " -f 10 #cut分割后,数第10个是ip
ifconfig | awk '/netmask/{print $2}' #awk 分割空格后,为null的不考虑,第二个不为空的为ip
11、综合应用案例
11.1 归档文件
需求:
实现一个每天对指定目录归档备份的脚本,输入一个目录名称(末尾不带/),
将目录下所有文件按天归档保存,并将归档日期附加在归档文件名上,
放在/root/archive下。
归档命令:tar
-c 表示归档,-z表示同时进行压缩,得到的文件后缀名为.tar.gz
脚本的实现:
==========================================================
#!/bin/bash
#首先判断输入参数的个数是否为1
if [ $# -ne 1 ]
then
echo "参数个数错误"
exit
fi
#从参数中获取目录名称
if [ -d $1 ]
then
echo #做后序处理
else
echo "目录不存在"
exit
fi
DIR_NAME=$(basename $1) #获取文件名称
DIR_PATH=$(cd $(dirname $1); pwd) #cd进到文件所在的目录,然后pwd获取绝对路径
#获取当前日期
DATE=$(date +%y%m%d)
#定义生成的归档文件名称
FILE=archive_${DIR_NAME}_${DATE}.tar.gz
DEST=/root/archive/${FILE}
#开始归档目录文件
echo "开始归档"
echo
tar -czf $DEST ${DIR_PATH}/${DIR_NAME} #前面是归档文件名,后面是要压缩的文件地址
if [ $? -eq 0 ]
then
echo "归档成功"
echo "归档文件为:$DEST"
else
echo "归档失败"
fi
exit
==========================================================
定时任务
crontab -e: 编辑当前用户的定时任务列表
crontab -l: 查看当前用户的定时任务列表
crontab -r: 删除当前用户的定时任务列表
11.2 发送消息