shell编程2-shell基础知识
1.编程语言分类
- 编译型语言:
程序在执行之前需要一个专门的编译过程,把程序编译成为机器语言文件,运行时不需要重新翻译,直接使用编译的结果就行了。程序执行效率高,依赖编译器,跨平台性差些。如C、C++
- 解释型语言:
程序不需要编译,程序在运行时由解释器翻译成机器语言,每执行一次都要翻译一次。因此效率比较低。比如Python/JavaScript/ Perl /ruby/Shell等都是解释型语言。
- 总结:
编译型语言比解释型语言速度较快,但是不如解释型语言跨平台性好。如果做底层开发或者大型应用程序或者操作系开发一般都用编译型语言;如果是一些服务器脚本及一些辅助的接口,对速度要求不高、对各个平台的兼容性有要求的话则一般都用解释型语言。
2.shell介绍
总结:
- shell就是人机交互的一个桥梁
- shell的种类
[root@test ~]# cat /etc/shells
/bin/sh #是bash shell的一个快捷方式
/bin/bash #bash shell是大多数Linux默认的shell,包含的功能几乎可以涵盖shell所有的功能
/sbin/nologin #表示非交互,不能登录操作系统
/bin/dash #小巧,高效,功能相比少一些
/bin/tcsh #是csh的增强版,完全兼容csh
/bin/csh #具有C语言风格的一种shell,具有许多特性,但也有一些缺陷
[root@shell ~]# cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
用户在终端(终端就是bash的接口)输入命令
bash是shell的一种类型
shell脚本
shell脚本就是由Shell命令组成的执行文件,将一些命令整合到一个文件中,进行处理业务逻辑,脚本不用编译即可运行。它通过解释器解释运行,所以速度相对来说比较慢。
命令 + 脚本基本格式 + 脚本特定语法 + 思想 = shell脚本
shell脚本应用场景
重复化,复杂化的工作,整理为一个脚本后仅需执行即可完成
例:
自动化批量部署openstack节点
自动化备份
自动化监控主机资源占用等
如何学习shell脚本
- 尽可能记忆更多的命令
- 掌握脚本的标准的格式(指定魔法字节、使用标准的执行方式运行脚本)
- 必须==熟悉掌握==脚本的基本语法(重点)
学习脚本的秘诀:
多看(看懂)——>多模仿(多练)——>多思考
脚本基本写法
#!/bin/bash
脚本第一行, #!魔法字符,指定脚本代码执行的程序。即它告诉系统这个脚本需要什么解释器来执行,也就是使用哪一种Shell
以下内容是对脚本的基本信息的描述
# Name: 名字
# Desc:描述describe
# Path:存放路径
# Usage:用法
# Update:更新时间
下面就是脚本的具体内容
commands
...
执行脚本
- 标准脚本执行方法(建议):(魔法字节指定的程序会生效)
[root@shell shell]# cat 1.sh
#!/bin/bash
# Name: llllyh812
# Desc: show time
# Path: /mnt/shell
# Usage: source /mnt/shell/1.sh
# Update: 2022/5/18
date
[root@shell shell]# chmod +x 1.sh
[root@shell shell]# ./1.sh
Wed May 18 22:02:39 CST 2022
- 非标准的执行方法(不建议):(魔法字节指定的程序不会运作)
[root@shell shell]# bash 1.sh
Wed May 18 22:08:15 CST 2022
[root@shell shell]# sh 1.sh
Wed May 18 22:08:18 CST 2022
[root@shell shell]# bash -x 1.sh
+ date
Wed May 18 22:08:56 CST 2022
# -x: 一般用于排错,查看脚本执行过程
[root@shell shell]# bash -n 1.sh
# -n: 用来查看脚本的语法是否有问题
# 如果脚本没有加可执行权限,不能使用标准的执行方法执行,bash 1.sh
[root@shell shell]# source 1.sh
Wed May 18 22:10:44 CST 2022
[root@shell shell]# . 1.sh
Wed May 18 22:10:46 CST 2022
# source 和 . 表示读取文件,执行文件里的命令
3.变量
变量的分类
- 本地变量: 当前用户自定义的变量.当前进程中有效,其他进程及当前进程的子进程无效.
- 环境变量: 当前进程有效,并且能够被子进程调用.
- 查看当前用户的环境变量: env
- 查看当前用户的所有变量(临时变量与环境变量): set
- 将当前变量变成环境变量: export
[root@shell ~]# A=test
[root@shell ~]# export A=test
# export临时将一个本地变量(临时变量)变成环境变量
[root@shell ~]# env | grep ^A
A=test
[root@shell ~]# echo export A=test >> /etc/profile
[root@shell ~]# source /etc/profile
# 永久生效
- 全局变量:全局所有的用户和程序都能调用,且继承,新建的用户也默认能调用.
$HOME/.bashrc 当前用户的bash信息(aliase、umask等)
$HOME/.bash_profile 当前用户的环境变量()
oracle——>oracle用户——>$oracle/.bash_profile——>export home_install=/u01/app/xxx
$HOME/.bash_logout 每个用户退出当前shell时最后读取的文件
/etc/bashrc 使用bash shell用户全局变量
grep --color=auto
umask
/etc/profile 系统和每个用户的环境变量信息
mycat_home=/usr/local/mycat/bin
export mycat_home
执行mycat命令
# mycat
$ mycat
用户登录系统读取相关文件的顺序:
/etc/profile——>$HOME/.bash_profile——>$HOME/.bashrc——>/etc/bashrc——>$HOME/.bash_logout
source /etc/bashrc
- 系统变量(内置bash中变量): shell本身已经固定好了它的名字和作用.
$?:上一条命令执行后返回的状态,当返回状态值为0时表示执行正常,非0值表示执行异常或出错
若退出状态值为0,表示命令运行成功
若退出状态值为127,表示command not found
若退出状态值为126,表示找到了该命令但无法执行(权限不够)
若退出状态值为1&2,表示没有那个文件或目录
$$:当前所在进程的进程号 echo $$ eg:kill -9 `echo $$` = exit 退出当前会话
$!:后台运行的最后一个进程号 (当前终端) # gedit &
!$ 调用最后一条命令历史中的参数
!! 调用最后一条命令历史
$#:脚本后面接的参数的个数
$*:脚本后面所有参数,参数当成一个整体输出,每一个变量参数之间以空格隔开
$@: 脚本后面所有参数,参数是独立的,也是全部输出
$0:当前执行的进程/程序名 echo $0
$1~$9 位置参数变量
${10}~${n} 扩展位置参数变量 第10个位置变量必须用{}大括号括起来
[root@shell ~]# cat 1.sh
#!/bin/bash
echo "\$0 = $0"
# 为了正常显示需要转义符\
echo "\$# = $#"
echo "\$* = $*"
echo "\$@ = $@"
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"
echo "\$12 = ${12}"
$*和$@的区别:
$* :表示将变量看成一个整体
$@ :表示变量是独立的
[root@shell ~]# source 1.sh 1 2 3
$0 = -bash
$# = 3
$* = 1 2 3
$@ = 1 2 3
$1 = 1
$2 = 2
$3 = 3
$12 =
[root@shell ~]# cat 2.sh
#!/bin/bash
for i in "$@"
do
echo $i
done
echo "========分割线=========="
for i in "$*"
do
echo $i
done
[root@shell ~]# source 2.sh 1 2 3
1
2
3
========分割线==========
1 2 3
- 什么时候需要定义变量
- 某个内容需要多次使用,在代码中重复出现,可以使用变量代表该内容,再次修改内容时,仅仅需要修改变量的值
- 保存某些命令的执行结果,为后续代码需要时,可以直接使用变量
- 变量的定义规则
1. 默认情况下,shell里定义的变量是不分类型的,可以给变量赋与任何类型的值;
等号两边不能有空格,对于有空格的字符串做为赋值时,要用引号引起来
变量名=变量值
2. 变量的获取方式: $变量名 ${变量名}
[root@shell ~]# a=123456
[root@shell ~]# echo ${a}
123456
[root@shell ~]# echo ${a:2:4}
3456
# a表示变量名;2表示从第三个字符开始(首字母下标为0);4表示后面4个字符
3. 取消变量: unset 变量名
[root@shell ~]# unset a
[root@shell ~]# echo ${a}
4. 变量名区分大小写,同名称但大小写不同的变量名代表不同变量
[root@shell ~]# a=123
[root@shell ~]# A=456
[root@shell ~]# echo $a
123
[root@shell ~]# echo $A
456
5. 变量名可以是字母或数字或下划线,但是不能以特殊字符或者数字开头
[root@shell ~]# 1a=123
-bash: 1a=123: command not found
[root@shell ~]# ~a=123
-bash: ~a=123: command not found
[root@shell ~]# _a=123
[root@shell ~]# echo $_a
123
6. 命令的执行结果可以保存到变量
[root@shell ~]# a=`uname`
[root@shell ~]# echo $a
Linux
[root@shell ~]# a=$(hostname)
[root@shell ~]# echo $a
shell.novalocal
7. 有类型变量 declare
-i 将变量看成整数
-r 使变量只读 readonly
-x 标记变量通过环境导出 export
-a 指定为索引数组(普通数组);查看普通数组
-A 指定为关联数组;查看关联数组
[root@shell ~]# a=1
[root@shell ~]# b=2
[root@shell ~]# echo $a+$b
1+2
[root@shell ~]# declare -i a=2
[root@shell ~]# declare -i a=1
[root@shell ~]# declare -i b=2
[root@shell ~]# declare -i c=$a+$b
[root@shell ~]# echo $c
3
[root@shell ~]# AAA=test
[root@shell ~]# env|grep AAA
[root@shell ~]# export AAA
[root@shell ~]# env|grep AAA
AAA=test
[root@shell ~]# declare -x BBB=test
[root@shell ~]# env|grep BBB
BBB=test
8.数组
普通数组:只能使用整数作为数组索引(元素的下标)
关联数组:可以使用字符串作为数组索引(元素的下标)
普通数组定义:用括号来表示数组,数组元素(变量)用"空格"符号分割开。定义数组的一般形式为:
一次赋一个值:
变量名=变量值
[root@shell ~]# array[0]=a1
[root@shell ~]# array[1]=a2
[root@shell ~]# array[2]=a3
一次赋多个值:
[root@shell ~]# array=(a1 a2 a3)
[root@shell ~]# array1=(`cat /etc/passwd`)
通过文件赋值,将文件每一行赋值给array1数组
[root@shell ~]# array2=(`ls /root`)
[root@shell ~]# array3=(1 2 3 4 [10]='test')
[root@shell ~]# echo ${array3[10]}
test
读取数组:
${array[i]} i表示元素的下标
使用@ 或 * 可以获取数组中的所有元素:
获取第一个元素
echo ${array[0]}
echo ${array[*]} 获取数组里的所有元素
echo ${#array[*]} 获取数组里所有元素个数
echo ${!array[@]} 获取数组元素的索引下标
echo ${array[@]:1:2} 访问指定的元素;1代表从下标为1的元素开始获取;2代表获取后面几个元素
查看普通数组信息:
[root@shell ~]# echo ${array[*]}
var1 var2 var3
[root@shell ~]# echo ${array[@]}
var1 var2 var3
[root@shell ~]# echo ${array[2]}
var3
[root@shell ~]# echo ${array[*]:1:2}
var2 var3
[root@shell ~]# echo ${#array[*]}
3
[root@shell ~]# echo ${!array[*]}
0 1 2
关联数组定义:
首先声明关联数组
declare -A asso_array1
declare -A asso_array2
declare -A asso_array3
数组赋值:
一次赋一个值:
数组名[索引|下标]=变量值
[root@shell ~]# asso_array1[name]=xiaom
[root@shell ~]# asso_array1[age]=13
[root@shell ~]# asso_array1[gender]=male
一次赋多个值:
[root@server ~]# asso_array2=([name1]=harry [name2]=jack [name3]=amy [name4]="Miss Hou")
查看关联数组:
[root@shell ~]# declare -A
declare -A asso_array1=([gender]="male" [name]="xiaom" [age]="13" )
declare -A asso_array2=([name3]="amy" [name2]="jack" [name1]="harry" [name4]="Miss Hou" )
declare -A asso_array3
[root@shell ~]# echo ${asso_array1[name]}
xiaom
[root@shell ~]# echo ${asso_array1[age]}
13
[root@shell ~]# echo ${asso_array1[gender]}
male
[root@shell ~]# echo ${asso_array}
${asso_array1} ${asso_array2}
#自动补全
[root@shell ~]# echo ${asso_array1[*]}
male xiaom 13
[root@shell ~]# echo ${!asso_array1[*]}
gender name age
[root@shell ~]# echo ${#asso_array1[*]}
3
[root@shell ~]# echo ${#asso_array2[*]}
4
[root@shell ~]# echo ${!asso_array2[*]}
name3 name2 name1 name4
9. 交互式定义变量的值 read 主要用于让用户去定义变量值
-p 提示信息
-n 字符数 (限制变量值的字符数)
-s 不显示
-t 超时(默认单位秒)(限制用户输入变量值的超时时间)
# 比较有用
[root@shell ~]# cat 1.txt
10.1.1.1 255.255.255.0
[root@shell ~]# read -p "Input your IP and Netmask:" ip mask < 1.txt
[root@shell ~]# echo $ip
10.1.1.1
[root@shell ~]# echo $mask
255.255.255.0
10. 其他变量(扩展)
1)取出一个目录下的目录和文件:dirname和 basename
2)变量"内容"的删除和替换
一个“%”代表从右往左去掉一个/key/
两个“%%”代表从右往左最大去掉/key/
一个“#”代表从左往右去掉一个/key/
两个“##”代表从左往右最大去掉/key/
[root@shell ~]# A=/root/1.txt
[root@shell ~]# echo $A
/root/1.txt
[root@shell ~]# dirname $A
/root
[root@shell ~]# basename $A
1.txt
[root@shell ~]# url=www.taobao.com
[root@shell ~]# echo ${#url}
14
# 获取变量的长度
[root@shell ~]# echo ${url#*.}
taobao.com
[root@shell ~]# echo ${url##*.}
com
[root@shell ~]# echo ${url%.*}
www.taobao
[root@shell ~]# echo ${url%%.*}
www
拓展:
替换:/ 和 //
[root@shell ~]# echo ${url/ao/AO}
www.tAObao.com
[root@shell ~]# echo ${url//ao/AO}
www.tAObAO.com
# 贪婪替换
替代: - 和 :- +和:+
[root@shell ~]# echo ${abc-123}
[root@shell ~]# abc=hello
[root@shell ~]# echo ${abc-444}
hello
[root@shell ~]# echo $abc
hello
[root@shell ~]# abc=
[root@shell ~]# echo ${abc-222}
${变量名-新的变量值} 或者 ${变量名=新的变量值}
变量没有被赋值:会使用“新的变量值“ 替代
变量有被赋值(包括空值): 不会被替代
[root@shell ~]# echo ${ABC:-123}
123
[root@shell ~]# ABC=HELLO
[root@shell ~]# echo ${ABC:-123}
HELLO
[root@shell ~]# ABC=
[root@shell ~]# echo ${ABC:-123}
123
${变量名:-新的变量值} 或者 ${变量名:=新的变量值}
变量没有被赋值或者赋空值:会使用“新的变量值“ 替代
变量有被赋值: 不会被替代
[root@shell ~]# echo ${abc=123}
[root@shell ~]# echo ${abc:=123}
123
[root@shell ~]# unset abc
[root@shell ~]# echo ${abc:+123}
[root@shell ~]# echo ${abc:+123}
[root@shell ~]# abc=hello
[root@shell ~]# echo ${abc:+123}
123
[root@shell ~]# abc=
[root@shell ~]# echo ${abc:+123}
${变量名+新的变量值}
变量没有被赋值或者赋空值:不会使用“新的变量值“ 替代
变量有被赋值: 会被替代
[root@shell ~]# unset abc
[root@shell ~]# echo ${abc+123}
[root@shell ~]# abc=hello
[root@shell ~]# echo ${abc+123}
123
[root@shell ~]# abc=
[root@shell ~]# echo ${abc+123}
123
${变量名:+新的变量值}
变量没有被赋值:不会使用“新的变量值“ 替代
变量有被赋值(包括空值): 会被替代
[root@shell ~]# unset abc
[root@shell ~]# echo ${abc?123}
-bash: abc: 123
[root@shell ~]# abc=hello
[root@shell ~]# echo ${abc?123}
hello
[root@shell ~]# abc=
[root@shell ~]# echo ${abc?123}
${变量名?新的变量值}
变量没有被赋值:提示错误信息
变量被赋值(包括空值):不会使用“新的变量值“ 替代
[root@shell ~]# unset abc
[root@shell ~]# echo ${abc:?123}
-bash: abc: 123
[root@shell ~]# abc=hello
[root@shell ~]# echo ${abc:?123}
hello
[root@shell ~]# abc=
[root@shell ~]# echo ${abc:?123}
-bash: abc: 123
${变量名:?新的变量值}
变量没有被赋值或者赋空值时:提示错误信息
变量被赋值:不会使用“新的变量值“ 替代
说明:?主要是当变量没有赋值提示错误信息的,没有赋值功能
4.简单的四则运算及条件判断
算术运算:默认情况下,shell就只能支持简单的整数运算
+ - * / %(取模,求余数)
Bash shell 的算术运算有四种方式:
1. 使用 $(( ))
2. 使用$[ ]
3. 使用 expr 外部程式
4. 使用let 命令
注意:
n=1
let n+=1 等价于let n=n+1
使用shell做小数运算
[root@shell ~]# echo 1+1.5|bc
2.5
# -bash: bc: command not found
# 使用这道命令时可能会出现无法找到bc命令
# dnf -y install bc 即可,安装计算器
i++ 和 ++i (了解)
对变量的值的影响:
[root@shell ~]# i=1
[root@shell ~]# let i++
[root@shell ~]# echo $i
2
[root@shell ~]# j=1
[root@shell ~]# let ++j
[root@shell ~]# echo $j
2
对表达式的值的影响:
[root@shell ~]# unset i j
[root@shell ~]# i=1;j=1
[root@shell ~]# let x=i++
# 先赋值,再运算
[root@shell ~]# let y=++j
# 先运算,再赋值
[root@shell ~]# echo $i
2
[root@shell ~]# echo $j
2
[root@shell ~]# echo $x
1
[root@shell ~]# echo $y
2
总结
$(()) $[]
expr 注意空格,*要进行转义 \
let n+=1 等价 let n=n+1
let n=n**5 n有初值,然后求次幂
i++ ++i
对变量本身没有影响(自己+1);
表达式中有影响;i++ 先赋值再运算 ++i先运算再赋值
let x=i++ let x=++i
条件判断(了解)
1.语法格式
- 格式1: test 条件表达式
- 格式2: [条件表达式]
- 格式3: [[ 条件表达式 ]] 支持正则 =~
说明:
man test去查看,很多的参数都用来进行条件判断
2.条件判断相关参数
- 与文件存在与否的判断(了解)
-e 是否存在 不管是文件还是目录,只要存在,条件就成立
-f 是否为普通文件
-d 是否为目录
-S socket
-p pipe
-c character
-b block
-L 软link
三种语法格式:
test -e file 只要文件存在条件为真
[ -d /shell01/dir1 ] 判断目录是否存在,存在条件为真
[ ! -d /shell01/dir1 ] 判断目录是否存在,不存在条件为真
[[ -f /shell01/1.sh ]] 判断文件是否存在,并且是一个普通的文件
-s 判断文件是否有内容(大小),非空文件条件满足
说明:-s表示非空,! -s 表示空文件
说明:1.sh文件里有内容的。
[root@shell ~]# test -s 1.txt
[root@shell ~]# echo $?
0
[root@shell ~]# test -s 1.sh
[root@shell ~]# echo $?
1
[root@shell ~]# test -s 1.txt
[root@shell ~]# echo $?
0
[root@shell ~]# touch aaa
[root@shell ~]# cat aaa
[root@shell ~]# test -s aaa
[root@shell ~]# echo $?
1
[root@shell ~]# test ! -s aaa
[root@shell ~]# echo $?
0
- 文件权限相关的判断
-r 当前用户对其是否可读
-w 当前用户对其是否可写
-x 当前用户对其是否可执行
-u 是否有suid
-g 是否sgid
-k 是否有t位
- 两个文件的比较判断
file1 -nt file2 比较file1是否比file2新
file1 -ot file2 比较file1是否比file2旧
file1 -ef file2 比较是否为同一个文件,或者用于判断硬连接,是否指向同一个inode
test file1 -nt file2
[ file1 -ot file2 ]
- 整数之间的判断
-eq 相等
-ne 不等
-gt 大于
-lt 小于
-ge 大于等于
-le 小于等于
- 字符串之间的判断
-z 是否为空字符串 字符串长度为0,就成立
-n 是否为非空字符串 只要字符串非空,就是成立
string1 = string2 是否相等
string1 != string2 不等
[root@shell ~]# AAA=hello
[root@shell ~]# BBB=world
[root@shell ~]# test -z $AAA
[root@shell ~]# echo $?
1
[root@shell ~]# test -n $AAA
[root@shell ~]# echo $?
0
[root@shell ~]# [ $AAA = $BBB ]
[root@shell ~]# echo $?
1
[root@shell ~]# [ $AAA != $BBB ]
[root@shell ~]# echo $?
0
- 多重条件判断
逻辑判断符号:
-a 和 && (and 逻辑与) 两个条件同时满足,整个大条件为真
-o 和 || (or 逻辑或) 两个条件满足任意一个,整个大条件为真
[ 1 -eq 1 -a 1 -ne 0 ] 整个表达式为真
[ 1 -eq 1 ] && [ 1 -ne 0 ]
[ 1 -eq 1 -o 1 -ne 1 ] 整个表达式为真
[ 1 -eq 1 ] || [ 1 -ne 1 ]
[root@shell ~]# [ 1 -eq 0 ] && echo true || echo false
false
[root@shell ~]# [ 1 -eq 1 ] && echo true || echo false
true
&&:前面的表达式为真
||:前面的表达式为假
总结:
1、; && ||都可以用来分割命令或者表达式
2、; 完全不考虑前面的语句是否正确执行,都会执行;号后面的内容
3、&& 需要考虑&&前面的语句的正确性,前面语句正确执行才会执行&&后的内容;反之亦然
make && make install
4、|| 需要考虑||前面的语句的非正确性,前面语句执行错误才会执行||后的内容;反之亦然
5、如果&&和||一起出现,从左往右依次看,按照以上原则
3.示例
数值比较:
[root@shell ~]# [ $(id -u) -eq 0 ] && echo "the user is admin"
the user is admin
[root@shell ~]# [ $(id -u) -ne 0 ] && echo "the user is not admin"
[root@shell ~]# [ $(id -u) -eq 0 ] && echo "the user is admin" || echo "the user is not admin"
the user is admin
# 判断用户是否为root
类C风格的数值比较:
注意:在(( ))中,=表示赋值;==表示判断
1159 ((1==2));echo $?
1160 ((1<2));echo $?
1161 ((2>=1));echo $?
1162 ((2!=1));echo $?
1163 ((`id -u`==0));echo $?
1209 ((a=123));echo $a
1210 unset a
1211 ((a==123));echo $?
字符串比较:
注意:双引号引起来,看作一个整体;= 和 == 在 [ 字符串 ] 比较中都表示判断
1196 a='hello world';b=world
1197 [ $a = $b ];echo $?
1198 [ "$a" = "$b" ];echo $?
1199 [ "$a" != "$b" ];echo $?
1200 [ "$a" !== "$b" ];echo $? 错误
1201 [ "$a" == "$b" ];echo $?
1202 test "$a" != "$b";echo $?
思考:[ ] 和 [[ ]] 有什么区别?
1213 a=
1214 test -z $a;echo $?
1215 a=hello
1216 test -z $a;echo $?
1217 test -n $a;echo $?
1217 test -n "$a";echo $?
# [ '' = $a ];echo $?
-bash: [: : unary operator expected
2
# [[ '' = $a ]];echo $?
0
1278 [ 1 -eq 0 -a 1 -ne 0 ];echo $?
1279 [ 1 -eq 0 && 1 -ne 0 ];echo $?
1280 [[ 1 -eq 0 && 1 -ne 0 ]];echo $?