关于shell
- 运维人员会写一些shell
- 管理集群/开发效率提高需要shell
- shell是一个命令解释器,接收用户的命令,然后调用操作系统内核
- 计算机
- 硬件-linux内核-shell-外层应用程序
- shell是翻译官,是桥梁
- shell可以一行行命令的执行,也可以一堆命令作为脚本来执行
- 因此,其可以当做一种编程语言
- 红帽系用的默认是bash,ubuntu用的是dash
第一个shell脚本
- 一般后缀是.sh (约定俗称)
- 第一句
#!/bin/sh
指定解析器 - 脚本执行方式
- bash或sh +脚本的绝对or相对路径 (一般不用)
- 直接输入脚本路径直接运行,需要x权限(chmod +x 脚本路径) ./hello.sh
- source or . +脚本路径(用的少)
- source就是一种shell内嵌, . 也差不多, 就是一种shell命令
- 前面两种是在当前的bash shell中起了一个子shell,不影响当前环境
- source是用的是当前shell
- 要考虑到作用范围, 父子之前是否需要交互
- 有时候改一些profile,需要source一下,就是这样的
shell中的变量
- 临时在shell编程中保存的信息
- 分类
- 系统or用户
- 系统一般以$开头
- echo $HOME 看某个变量含义
- env 看所有系统定义的全局变量
- set 看所有变量
- 全局(当前shell)or局部(子shell)
- 系统or用户
- 自定义变量
- 定义变量 变量名=变量值 =前后不能有空格
- 空格是命令的标识
- 撤销变量 unset 变量名
- 声明静态变量 readonly变量, 不能unset,不能改
- readonly是静态变量,不可修改撤销
- 直接定义的变量是局部变量
- export 变量名 设置为全局变量
- 如果有空格,需要引号包括
- 定义变量 变量名=变量值 =前后不能有空格
- 父shell定义的全局变量
- 子shell可以看到
- 子shell可以修改,但只限于自己这个环境修改有效,对其他环境修改失败,即使你export,也不行
- 只能在父shell中修改
- 其次,子shell中定义的全局变量,在子shell exit后会消失
- 自定义变量值默认是字符串
- 运算需要shell中的运算符
- 特殊变量
- 输入参数
- $n表示1-9个参数,如果十以上,用大括号包含,0代表当前脚本名称
- 在字符串中,单引号会将$认为是字符串,而双引号会认为是变量
- $#获取输入参数个数,一般用于循环
- $*将所有参数做成一个整体,
- $@也是获取所有参数,但是分开对待,类似于数组
- 如果没有引号包括,二者都是一样的,都是数组
- $?最后一次执行命令的返回状态 0 为正常执行, 其他是非零,有报错含义
- 随便什么命令
运算符
- $((运算式))
- $[运算式]
条件判断
- test 条件 (条件中的等于号一定要空格,不然就一定为0,判断用空格,赋值不用)
- 正确返回0
- 错误返回1
- 简写: 直接[ condition ] (前后一定要空格)
- 这里可以用双小括号,就方便很多了
- 主要一定要空格,连在一起,就会认为是一个值,就会出问题
- !=为不等于
- 常用判断条件
- 整数之间
- -eq 等于 equal
- -ne不等于 notequal
- -lt小于 less than
- -le小于等于 less equal
- -gt大于 greater than
- -ge大于等于 greater equal
- 文件权限
- -r 可读
- -w 可写
- -x 可执行
- 文件类型
- -e 文件存在
- -f 文件类型
- -d文件存在且是目录
- 整数之间
- 多条件: && ||
- [condition] && xxx1 || xxx2 (这里就是if-else)
流程控制
-
if [ condition ] then xxx elif [ condition ] then xxx fi 可以用;代表换行
-
为了防止输入参数为空报错
- [ $1 = “xxx”]
- 改为 [ "$1"s = "xxx"s ] 拼接一个字符串,保证一定非空
-
case语句
-
case $变量名 in "值1") xxx ;; "值2") xxxx ;; *) xxx ;; esac
-
for循环
-
for ((初始值;循环控制条件;条件变化)) do 程序 done
-
双小括号里可以直接数学运算
-
for 变量 in 值1 值2 值3 do 程序 done
-
{}表示一段序列
- {1…100} 从1-100的序列
while 循环
-
while [condition] do 程序 done
read读取控制台输入
-
read (选项) (参数) -p 指定读取值时的提示符 -t 等待时间
函数
-
一段代码的集合
-
脚本本质上也是函数
- 脚本没有返回值
- 脚本的调用过程太笨重
-
系统函数
-
使用系统函数 $(系统函数)
-
date 当前时间
- +%s 时间戳
-
basename 路径
- 获得最后一个斜杠后面的文件名称
- 参数 suffix 例如 basename xxx.txt .txt 删除后缀
-
dirname
-
获得目录路径,去除文件名
-
获得当前绝对路径
-
cd $(dirname $0) pwd 进入当前文件的目录,然后pwd打印目录信息
-
-
-
-
自定义函数
-
[function]funname[()] { action; [return int;] }
-
这里的return只能在0-255之间,int就很蠢
-
所以实际接收
-
此时当结果超过255,则不正确
-
所以return其实不好用
-
可以这么来
-
实际上$()获得的是函数中最后一行命令的执行结果
- 这句话是老师说,但我测试之后感觉不对,如此操作,会有两个返回值
- 感觉echo在函数中,就是返回的意思,而非打印
-
归档文件 综合应用联系
-
实现一个脚本,每天对指定目录进行归档备份
- 输入一个目录名称
- 将目录下的所有文件按天归档保存,将归档日期附加在文件名上,放在/root/archive下
-
脚本内容分析
-
# 首先判断输入参数个数是否为1 if [ $# != 1 ] then echo "参数个数错误,只需要1个参数" exit fi # 从参数中获取目录名称 if [ -d $1 ] then echo else echo echo "目录不存在" echo exit fi # 转换为绝对路径 DIR_NAME=$(basename $1) DIR_PATH=$(cd $(dirname $1);pwd) # 获取当前日期 DATE=$(date +%y%m%d%H%M) # 定义生成的归档文件名称 FILE=archive_${DIR_NAME}_$DATE.tar.gz DEST=/root/archive/$FILE # 开始归档目录文件 echo "开始归档..." echo # c归档 z压缩 f可视化 生成文件名称 归档目录名称 tar -czf $DEST $DIR_PATH/$DIR_NAME # 是否执行成功 if [ $? -eq 0 ] then echo echo "归档成功!" echo "归档文件为:$DEST" echo else echo "归档出现问题" echo fi exit
-
exit为退出
-
tar实际上就是备份,归档
-
-
定时任务
-
crontab -l 查看当前定时任务
-
crontab -e修改定时任务
-
# 分 时 日 周 月 脚本路径 参数 (定时执行) 0 2 * * * /root/scripts/daily_archive.sh /root/scripts
-
关于echo
- 明显,在函数中其作用为函数的返回值,可以多次返回,但和return这种是不一样的
- 而在最外层命令执行时,它会打印
- 因此,可以这样理解,实际上,它就是将值往外一层返回
- bash为一层,函数再往里一层,只有到bash外,才算打印
- 这样就可以理解的通了,虽然这样理解可能不对,但