1、变量
1.1 变量类型
变量类型:
- 内置变量,如:PS1,PATH,UID,HOSTNAME,$$,BASHPID,PPID,$?,HISTSIZE
- 用户自定义变量
1.2 Shell变量命名法则
- 不能使用程序中保留字:如 if,for
- 只能使用数字、字母及下划线,且不能以数字开头,不支持短横线 "-", 和主机名相反
- 见名知义,用英文单词命名,并体现出时间作用,不要用简写
- 统一命名规则: 驼峰命名法 studentname,大驼峰 StudentName 小驼峰 studentName
- 变量名大写:STUDENT_NAME
- 局部变量小写
- 函数名小写
变量赋值:
name=‘value‘
value可以是以下多种形式
直接字串:name=‘root‘
变量引用:name="$USER"
命令引用:Name=`COMMAND` 或 name=$(COMMAND)
1.3 位置变量
位置变量: bash shell中内置的变量,在脚本中通过命令行传递给脚本的参数
$1,$2,... 对应第1个,第2个等参数
$0 命令本身,包括路径
$* 传递脚本的所有参数,全部参数合为一个字符串
$@ 传递脚本的所有参数,每个参数为独立字符串
$# 传递给脚本的参数的个数
set -- 清空所有位置变量
范例:
[root@CentOS8 data]#cat arg.sh
#!/bin/bash
echo "1st arg is $1"
echo "2st arg is $2"
echo "3st arg is $3"
echo "10st arg is ${10}"
echo "11st arg is ${11}"
echo "The number of arg is $#"
echo "All args are $*"
echo "All args are $@"
echo "The scriptname is `basename $0`"
[root@CentOS8 data]#bash arg.sh {a..z}
1st arg is a
2st arg is b
3st arg is c
10st arg is j
11st arg is k
The number of arg is 26
All args are a b c d e f g h i j k l m n o p q r s t u v w x y z
All args are a b c d e f g h i j k l m n o p q r s t u v w x y z
The scriptname is arg.sh
1.4 退出状态码
$?的值为0 #代表成功
$?的值1-255 #代表失败
2、 条件测试命令
- test EXPRESSION
- [ EXPRESSION ] [ ] 和 test等价
- [[ EXPRESSION ]]
2.1 变量测试
#判断 NAME 变量是否定义
[ -v NAME ]
#判断 NAME 变量是否定义并且是名称引用
[ -R NAME ]
2.2 数值测试
-eq 是否等于
-ne 是否不等于
-gt 是否大于
-ge 是否大于等于
-lt 是否小于
-le 是否小于等于
2.3 字符串测试
-z STRING 字符串是否为空,没有定义或空为真,不空为假
-n STRING 字符串是否不空,不空为真,空为假
STRING 同上
STRING1 = STRING2 是否等于
STRING1 != STRING2 是否不等于
[[ ]] 建议当使用正则表达式或通配符使用,一般情况使用[]
== 左侧字符串是否和右侧的PATTERN相同, PATTERN为通配符
=~ 左侧字符串是否被右侧的正则表达式的PATTERN所匹配, PATTERN为扩展的正则表达式
2.4 文件测试
#存在性测试
-a FILE 如果文件存在,则为True,同 -e
-b FILE 如果FILE是块设备文件则为True。
-c FILE 如果FILE是字符文件为True。
-d FILE 如果FILE是目录则为True。
-e FILE 如果文件存在,则为True。
-f FILE 如果文件存在且为普通文件则为True。
-h FILE 如果FILE是符号链接则为True。
-L FILE 如果FILE是符号链接则为True。
-p FILE 如果FILE是一个命名管道则为True。
-S FILE 如果FILE是套接字,则为True。
-k FILE 如果FILE设置了“sticky”位,则为True。
-s FILE 如果文件存在且不为空,则为True。
-t FD 如果在终端上打开FD,则为True。
-u FILE 如果文件为set-user-id则为True。
-r FILE 如果文件是可读的,则为True。
-w FILE 如果文件是可写的,则为True。
-x FILE 如果文件是可执行的,则为True。
-O FILE 如果文件实际上属于您,则为True。
-G FILE 如果文件实际上属于你的组,则为True。
-N FILE 如果文件在上次读取后被修改过,则为True。
FILE1 -nt FILE2 如果FILE1比FILE2更新则为True(根据修改日期)。
FILE1 -ot FILE2 如果FILE1比FILE2老,则FILE2为True。
FILE1 -ef FILE2 如果FILE1是FILE2的硬链接,则FILE2为True
2.5 ( ) 和 { }
(CMD1;CMD2;...) 和 { CMD1;CMD2;...;} 都可以将多个命令组合在一起,批量执行
(list) 会开启子shell,并且list中变量赋值及内部命令执行后,将不再影响后续的环境
{list;} 不会开启子shell,在当前shell中运行,会影响当前shell环境
2.6 使用read命令来接受输入
使用read来把输入值分配给一个或多个shell变量,read从标准输入中读取值,给每个单词分配一个变量,所有剩余单词都被分配给最后一个变量,如果变量没有指定,默认标准输入的值赋值给系统内置变量REPLY
常见选项:
-p 指定要显示的提示
-s 静默输入,一般用于密码
-n N 指定输入的字符长度N
-d ‘字符‘ 输入结束符
-t N TIMEOUT为N秒
3、流程控制
3.1 选择执行 if 语句
格式:
if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi
单分支
if 判断条件; then
条件为真的分支脚本
fi
双分支
if 判断条件; then
条件为真的分支代码
else
条件为假的分支代码
fi
多分支
if 判断条件1; then
条件1为真的分支代码
elif 判断条件2; then
条件2为真的分支代码
elif 判断条件3; then
条件3为真的分支代码
...
else
以上条件都为假的分支代码
fi
3.2 条件判断 case 语句
格式:
case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac
case 变量引用 in
PAT1)
分支1
;;
PAT2)
分支2
;;
...
*)
默认分支
;;
esac
4、循环
4.1 for 循环
格式1:
for NAME [in WORDS ... ]; do COMMANDS; done
for 变量名 in 列表;do
循环体
done
格式2:
双小括号法,即((...))格式,可以用于算术运算
for ((: for (( exp1;exp2;exp3 )); do COMMANDS; done
for ((控制变量初始化;条件判断表达式;控制变量的修正表达式));
do
循环体
done
4.2 while 循环
while COMMANDS; do COMMANDS;done
while CONDITION; do
循环体
done
说明:
CONDITION: 循环控制条件;进入循环前,先做一次判断,每一次循环之后会再次做判断;条件为真,则执行一次循环;直到条件为"false"终止循环。
4.3 until 循环
until COMMANDS; do COMMANDS; done
until CONDITION; do
循环体
done
进入条件: CONDITION 为 false
退出条件: CONDITION 为 ture
4.4 循环控制语句 continue
continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断,最内层为第1层
while CONDITION1; do
CMD1
...
if CONDITON2; then
continue
fi
CMD
...
done
范例:
[root@CentOS8 scripts]#cat continue_for.sh
#!/bin/bash
for ((i=0;i<10;i++)); do
for((j=0;j<10;j++)); do
[ $j -eq 5 ] && continue 2
echo $j
done
echo
done
[root@CentOS8 scripts]#bash continue_for.sh
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
4.5 循环控制语句 break
break [N]:提前结束第N层的整个循环,最内层为第1层
while CONDITION1; do
CMD1
...
if CONDITON2; then
break
fi
CMD
...
done
范例:
[root@CentOS8 scripts]#cat break_for.sh
#!/bin/bash
for ((i=0;i<10;i++)); do
for((j=0;j<10;j++));do
[ $j -eq 5 ] && break 2
echo $j
done
echo
done
[root@CentOS8 scripts]#bash break_for.sh
0
1
2
3
4
[root@CentOS8 scripts]#
5、函数
5.1 定义函数
#语法1:
func_name () {
...函数体...
}
#语法2:
function func_name {
...函数体...
}
#语法3:
function func_name () {
...函数体...
}
5.2 查看函数
#查看当前已定义的函数名
declare -f func_name
#查看当前已定义的函数名定义
declaer -F func_name
#删除函数
unset func_name
5.3 函数调用
函数的调用方式
- 可在交互式环境下定义函数
- 可将函数放在脚本文件中作为脚本的一部分
- 可放在只包含函数的单独文件中
5.3.1 交互式环境调用函数
[root@CentOS8 scripts]#dir () {
> ls -l
> }
[root@CentOS8 scripts]#dir
total 12
-rw-r--r--. 1 root root 243 Mar 8 20:30 arg.sh
5.3.2 在脚本中定义及使用函数
函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell首次发现它后才能使用,调用函数使用函数名即可。
[root@CentOS8 scripts]#cat func1.sh
#!/bin/bash
#name:func1
hello(){
echo "Hello there today‘s date is `date +%F`"
}
echo "now going to the function hello"
hello
echo "back from the function"
[root@CentOS8 scripts]#bash func1.sh
now going to the function hello
Hello there today‘s date is 2021-03-08
back from the function
5.3.3 使用函数文件
可以将常使用的函数存入一个单独的函数文件,然后将函数载入shell,再进行函数调用
文件名可任意定义,但最好与相关任务有某种类型。
一旦函数文件载入shell,就可以在命令行或脚本中调用函数,可以使用delcare -f 或 set 命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数
若要改动函数,首先用unset命令从shell中删除函数,改动完毕后,再重新载入此文件
实现函数文件的过程:
- 创建函数文件,只存放函数的定义
- 在shell脚本或交互式shell中调用函数文件,格式如下:
. filename 或 source filename
6、脚本进阶实例
- 编写脚本 createuser.sh,实现如下功能:使用一个用户名做为参数,如果 指定参数的用户存在,就显示其存在,否则添加之;显示添加的用户的id号等信息
[root@CentOS8 scripts]#cat createuser.sh
#!/bin/bash
read -p "请输入一个用户名:" NAME
if `id $NAME &> /dev/null`;then
echo "$NAME已存在,id信息为: `id $NAME`"
else
PASSWD=`cat /dev/urandom |tr -cd [:alpha:] |head -c8`
`useradd $NAME &> /dev/null`
`echo "$PASSWD" |passwd --stdin $NAME &> /dev/null`
echo "用户名:$NAME 密码:$PASSWD" >> /data/user.txt
`chage -d 0 $NAME`
echo "$NAME已添加,id信息为:`id $NAME` 密码为:$PASSWD"
fi
[root@CentOS8 scripts]#bash createuser.sh
请输入一个用户名:grain
grain已添加,id信息为:uid=1001(grain) gid=1001(grain) groups=1001(grain) 密码为:NodUrJyB
[root@CentOS8 scripts]#bash createuser.sh
请输入一个用户名:grain
grain已存在,id信息为: uid=1001(grain) gid=1001(grain) groups=1001(grain)
- 编写生成脚本基本格式的脚本,包括作者,联系方式,版本,时间,描述等
vim /etc/vimrc
set cul
set ts=4
set paste
set autoindent
autocmd BufNewFile *.sh exec ":call SetTitle()" func SetTitle()
if expand("%:e") == ‘sh‘
call setline(1,"#!/bin/bash")
call setline(2,"#")
call setline(3,"#***********************************************************")
call setline(4,"#Author: grain")
call setline(5,"#TIM: 37xxxxxx")
call setline(6,"#Email: 37xxxxxx@qq.com")
call setline(7,"#Date: ".strftime("%Y-%m-%d"))
call setline(8,"#FileName: ".expand("%"))
call setline(9,"#Version: 1.0")
call setline(10,"#Description: The New Script")
call setline(11,"#Copyright (C): ".strftime("%Y")." All rights reserved")
call setline(12,"#***********************************************************")
call setline(13,"")
endif
endfunc
7、文件查找
文件查找:locate、find
? 非实时查找(数据库查找):locate
? 实时查找:find
7.1 locate
locate 查询系统上预建的文件索引数据库/var/lib/locate/mlocate.db
索引构建过程需要遍历整个根文件系统,很消耗资源,执行update可以更新数据库
工作特点:
- 查找速度快
- 模糊查找
- 非实时查找
- 搜索的是文件的全路径,不仅仅是文件名
- 可能只搜索用户具备读取和执行权限的目录
格式:
locate [OPTION]...[PATTERN]...
常用选项
-i 不区分大小写搜索
-r 使用基本正则表达式
-n N 只列举前N个匹配项目
7.2 find
find是事实查找工具,通过遍历指定路径完成文件查找
工作特点:
- 查找速度略慢
- 精确查找
- 实时查找
- 查找条件丰富
- 可能只搜索用户具备读取和执行权限的目录
格式:
find [OPTION]...[查找路径][查找条件][处理动作]
7.2.1 指定搜索目录层级
-maxdepth level 最大搜索目录深度
-mindepth level 最小搜索目录深度
-depth -d 对每个目录先处理目录内文件,再处理目录本身
find /etc -maxdepth 2 -mindepth 2
7.2.2 根据文件名和inode查找
-name #文件名称,支持glob通配符
-iname #文件名称,不区分大小写
-inum n #按inode号查找
-samefile name #相同inode号的文件
-links n #链接数为n的文件
-regex "PATTERN" 以PATTERN匹配整个文件路径,非文件名称
7.2.3 根据属主、属组查找
-user username:查找属主为指定用户的文件
-group grpname: 查找属组为指定组的文件
-uid uid:查找属主为指定uid号的文件
-gid gid:查找属组为指定gid号的文件
-nouser:查找没有属主的文件
-nogroup:查找没有属组的文件
7.2.4 根据文件类型查找
-type
TYPE可以是以下类型:
f: 普通文件
d: 目录文件
l: 符号链接文件
s: 套接字文件
b: 块设备文件
c: 字符设备文件
p: 管道文件
-empty 空文件或目录
-path 排除目录
find /data -type d -empty
#查找/etc/下,除/etc/sane.d目录的其它所有.conf后缀的文件
find /etc -path ‘/etc/sane.d‘ -a prune -o -name "*.conf"
7.2.5 组合条件
-a 与,默认多个条件是与关系
-o 或
-not 非 !
#查找/tmp目录下,属组不是root,且文件名不以f开头的文件
find /tmp ( -not -user root -a -not -name ‘f*‘ )-ls
7.2.6 根据文件大小查找
-size [+|-]#UNIT
常用单位:k, M, G, C(byte),大小写敏感
#UNIT: (#-1,#]
如:6k 表示(5k,6k]
-#UNIT: [0,#-1]
如:-6k 表示[0,5k]
+#UNIT: (#,∞)
如:+6k 表示(6k,∞)
find / -size +10G
7.2.7 根据时间戳
以"天"为单位
-atime [+|-]#
#: [#,#+1)
+#: [#+1,∞]
-#:[0,#)
-mtime
-ctime
以"分钟"为单位
-amin
-mmin
-cmin
7.2.8 根据权限查找
-perm [/|-] MODE
MODE: 精确权限匹配
/MODE: u,g,o有一个匹配即可,或关系
-MODE: u,g,o每个人都拥有指定权限才匹配,与关系
0 表示不关注
7.2.9 处理动作
-print:默认的处理动作,显示至屏幕
-ls: 类似对找到的文件执行 ls -l命令
-fls file:查找到的所有长格式信息保存到指定文件中,相当于 -ls > file
-delete: 删除查找到的文件
-ok COMMAND {} \; 对查找到的每个文件执行由COMMAND指定的命令,对每个文件执行命令前,都会交互式要求用户确认
-exec COMMAND {} \; 对查找到的每个文件执行由COMMAND指定的命令
{}: 用于引用查找到的文件名称自身
#查找/data下权限为644,后缀为sh的普通文件,增加执行权限
find /data -type f -perm 644 -name "*.sh" -exec chmod 755 {} \;
7.3 参数替换 xargs
xargs用于产生某个命令的参数,可以读入stdin的数据,并且以空格符或回车符将stdin的数据分割成为参数
find 和 xargs 的组合
find | xargs COMMAND
#批量创建和删除用户
echo user{1..10} | xargs -n1 useradd
echo user{1..10} | xargs -n1 userdel -r
8、 压缩和解压缩
8.1 compress 和 uncompress
格式
compress options [file...]
uncompress file.z #解压缩
-d 解压缩,相当于uncompress
-c 结果输出至标准输出,不删除原文件
-v 显示详情
bzat file.z 不显示解压缩的前提下查看文本文件内容
8.2 gzip 和 gunzip
常用选项
gzip [option]... file ...
-d 解压缩,相当于gunzip
-c 结果输出至标准输出,不删除原文件
-# 指定压缩比,#取值为1-9,值越大压缩比越大
8.3 bzip2 和 bunzip2
bzip2 [option]... file ...
-d 解压缩,相当于bunzip2
-c 结果输出至标准输出,不删除原文件
-# 指定压缩比,#取值为1-9,默认为9
-k keep,保留原文件
8.4 xz 和 unxz
xz [option]... file ...
-k keep,保留原文件
-d 解压缩
-c 结果输出至标准输出,保留原文件不改变
-# 压缩比,取值1-9,默认为6
一般压缩率 compress < gzip < bzip2 < xz ,但压缩率越高,消耗的系统资源越大,同时4个工具都只能压缩文件。
8.5 zip 和 unzip
zip可以实现打包目录和多个文件成一个文件并压缩,但可能会丢失文件属性信息。一般使用tar代替。
9、打包和解包
9.1 tar
tar可以对目录和多个文件打包成一个文件并压缩,保留文件属性不丢失。
tar [option]...
#打包归档,保留权限
tar -cvf /PATH/FILE.tar FILE...
#追加文件至归档
tar -rf /PATH/FILE.tar FILE...
#解包归档
tar xf /PATH/FILE.tar
tar xf /PATH/FILE.tar -C /data
9.2 查找打包实例
- 查找/etc目录下大于1M且类型为普通文件的所有文件
find /etc -size +1M -type f -ls
- 打包/etc/目录下面所有conf结尾的文件,压缩包名称为当天的时间,并拷贝到/usr/local/src目录备份
[root@CentOS8 scripts]#find /etc/ -name "*.conf"|xargs tar -cvf /usr/local/src/`date +%F`.tar.gz
[root@CentOS8 scripts]#ls /usr/local/src/
2021-03-10.tar.gz
- 查找当前系统上没有属主或属组,且最近一个周内曾被访问过的文件或目录
find / \( -nouser -o -nogroup \) -a -atime -7
- 查找/etc目录下至少有一类用户没有执行权限的文件
find /etc -not -perm /111