bash shell 脚本编程
编程3种处理方式:
1.顺序处理:按从上到下的处理方式
2.选择处理:根据条件不同选择不同的处理方式
3.循环处理:执行过程中多次执行某项程序
脚本:
本地执行远程脚本:
root@ubuntu2204 ~]# curl http://10.0.0.157/test.sh -s | bash
hello, world
Hello, world!
root@ubuntu2204 ~]# curl http://10.0.0.157/test.sh 2>/dev/null | bash
hello, world
Hello, world!
root@ubuntu2204 ~]# wget -qO - http://10.0.0.157/test.sh | bash
hello, world
Hello, world!
在远程主机运行本地脚本:
root@ubuntu2204 ~]# cat test2.sh
#!/bin/bash
hostname -I
#本地执行
root@ubuntu2204 ~]# bash test2.sh
10.0.0.150
#远程主机上执行
root@ubuntu2204 ~]# ssh root@10.0.0.157 /bin/bash < test2.sh
root@10.0.0.157's password:
10.0.0.157
备份脚本:
#!/bin/bash
echo -e "\033[1;32mStarting backup...\033[0m"
sleep 2
cp -av /etc/ /data/etc`date +%F`/
echo -e "\033[1;32mBackup is finished\033[0m"
脚本调试:
bash -n name .sh
只能检测脚本的语法错误 不能检测脚本的命令错误 也不会执行脚本
bash -x name .sh
逐行执行命令输出结果
常见的错误有三种:
1.语法错误:会导致后续命令不继续执行 可用bash -n 检查 提示的出错行不一定准确
2.命令错误:默认后续命令还会继续执行,可用bash -x 检查
3.逻辑错误:只能用bash -x 进行观察
获取一天后的时间
date +%F --date="+ day"
变量的定义和引用
普通变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell 进程均无效
环境变量:生效范围为当前shell进程及其子进程
本地变量:生效范围为当前shell进程中某代码片断,通常指函数
name='value'
value 可以是以下多种形式
name='root' #直接字串
name="$USER" #变量引用
name=`COMMAND` #命令引用
name=$(COMMAND) #命令引用
变量赋值只是临时生效,推出终端后变量会自动删除,无法持久保存
变量引用:
$name
${name}
弱引用和强引用
“$name”弱引用,其中的变量会被替换成变量值
'$name'强引用,其中的变量引用不会被替换成变量值,保持字符串
[root@ubuntu2204 ~]# TITLE='cto'
[root@ubuntu2204 ~]# echo "I am $TITLE"
I am cto
[root@ubuntu2204 ~]# echo 'I am $TITLE'
I am $TITLE
set: 查看定义变量
unset: 删除变量
只读变量
--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
查看只读变量
readonly name
declare -r name
范例:
[root@ubuntu2204 ~]# readonly PI=3.14159
[root@ubuntu2204 ~]# echo $PI
3.14159
[root@ubuntu2204 ~]# PI=3.14
-bash: PI: readonly variable
[root@ubuntu2204 ~]# unset PI
-bash: unset: PI: cannot unset: readonly variable
位置变量:
$1,$2,... #对应第1个、第2个等参数,shift [n]换位置
$0 #命令本身,包括路径
$* #传递给脚本的所有参数,全部参数合为一个字符串
$@ #传递给脚本的所有参数,每个参数为独立字符串
$# #传递给脚本的参数的个数
#注意:$@ $* 只在被双引号包起来的时候才会有差异
清空位置变量
set --
利用软链接实现同一个脚本不同功能
[root@ubuntu2204 ~]# cat test.sh
#!/bin/bash
echo $0
[root@ubuntu2204 ~]# ln -s test.sh a.sh
[root@ubuntu2204 ~]# ln -s test.sh b.sh
[root@ubuntu2204 ~]# ./a.sh
./a.sh
[root@ubuntu2204 ~]# ./b.sh
./b.sh
脚本安全和set
选项 | 含义 | 备注 |
---|---|---|
h | hashall | 开启hash表缓存外部命令路径 |
i | interactive-comments | 表示当前是交互是shell |
m | monitor | 开启监控模式,可通过job control来控制进程的停止.继续,后台或者前台执行等 |
b | braceexpand | 是否支持大括号扩展 |
h | history | 是否可用!快速展开history命令 |
echo $- 查看器相关配置
关闭hash缓存功能
[root@ubuntu2204 ~]# echo $-
himBHs
[root@ubuntu2204 ~]# hash
hits command
6 /usr/bin/bash
2 /usr/bin/cat
6 /usr/bin/vim
[root@ubuntu2204 ~]# set +h
[root@ubuntu2204 ~]# echo $-
imBHs
[root@ubuntu2204 ~]# hash
-bash: hash: hashing disabled
关闭大括号
[root@ubuntu2204 ~]# echo $-
imBHs
[root@ubuntu2204 ~]# echo {1..5}
1 2 3 4 5
[root@ubuntu2204 ~]# set +B
[root@ubuntu2204 ~]# echo $-
imHs
[root@ubuntu2204 ~]# echo {1..5}
{1..5}
set命令实现脚本安全:
字符 | 作用 |
---|---|
u | 开启此项,在使用一个没有声明的变量,会报错并且终止程序 |
e | 开启此项,命令返回非0就退出,不再继续执行后面的代码 |
o | set -o 显示所有 |
x | set -x |
-u 在扩展一个没有的变量时会报错
-e 如果一个命令返回一个非0退出状态值(失败)就退出, 等同set -o errexit
-o option 显示,打开或者关闭选项 显示选项:set -o 打开选项:set -o 选项 关 闭选项:set +o 选项
-x 当执行命令时,打印命令及其参数,类似 bash -x
范例
限制没有声明的变量
[root@ubuntu2204 ~]# cat set2.sh
#!/bin/bash
set -u
DIR=/test
rm -rf ${DIr}/* #这个变量其实不存在,如果没有 set -u 选项,则会删除根目录
rm -rf /*
[root@ubuntu2204 ~]# bash set2.sh
set2.sh: line 17: DIr: unbound variable
遇到错误会终止
[root@ubuntu2204 ~]# vim set3.sh
#!/bin/bash
set -e
echo 123
cd /test2/ #这个目录不存在,如果没有 set -e,则也会删根
rm -rf *
echo 456
#遇到错误行就终止
[root@ubuntu2204 ~]# bash set3.sh
123
set3.sh: line 17: cd: /test2/: No such file or directory
printf 的命令和常用选项
替换符 | 功能 |
---|---|
%s | 字符串 |
%d,%i | 十进制 |
%f | 浮点格式 |
%c | 显示对应参数的第一个字符 |
%b | 相对应的参数中包含转义字符时,可以使用此替换符进行替换,对应的转义字符会被转 义 |
%o | 八进制值 |
%u | 不带正负号的十进制 |
%x | 十六进制值(a-f) |
%X | 十六进制值(A-F) |
%% | 表示本身 |
说明:
%[N]s # N表示输出宽度,不够补空格 -N 表示左对齐
%[0][N]d # 0表示宽度不够时在左边补0,N表示输出宽度
%[.N]f # .N 表示保留的小数位
常见的转义字符:
转移符 | 功能 |
---|---|
\a | 警告字符,通常为ASCII的BEL字符 |
\b | 后退 |
\f | 换页 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ | 本身 |
算术运算:
[root@ubuntu2204 ~]# i=123;j=456;
[root@ubuntu2204 ~]# let k=i+j;echo $k
579
[root@ubuntu2204 ~]# k=$[i+j];echo $k
579
expr命令
#表达式中间要有空格
[root@ubuntu2204 ~]# expr 10+20
10+20
[root@ubuntu2204 ~]# expr 10 + 20
30
[root@ubuntu2204 ~]# expr 10 \* 20
200
逻辑运算:
与:& 一假则假,全真才真
<------|------|------|------|------|------|------|------|------|---
2^7 2^6 2^5 2^4 2^3 2^2 2^1 2^0
128 64 32 16 8 4 2 1
范例:
[root@ubuntu2204 ~]# x=$[2&6]
[root@ubuntu2204 ~]# echo $x
2
[root@ubuntu2204 ~]# x=$[7&3]
[root@ubuntu2204 ~]# echo $x
3
2为10 6为110
或:| 一真则真,全假才假
范例:
[root@ubuntu2204 ~]# x=$[2|6]
[root@ubuntu2204 ~]# echo $x
6
[root@ubuntu2204 ~]# x=$[7|3]
[root@ubuntu2204 ~]# echo $x
7
异或:异或的两个值,相同为假,不同为真。两个数字X,Y异或得到结果Z,Z再和任意两者之一X异或,将得出另一个值Y
变量互换:
[root@ubuntu2204 ~]# x=10;y=20;temp=$x;x=$y;y=$temp;echo x=$x,y=$y
x=20,y=10
[root@ubuntu2204 ~]# x=10;y=20;x=$[x^y];y=$[x^y];x=$[x^y];echo x=$x,y=$y
x=20,y=10
短路运算:
短路与 &&
cmd1 | cmd2 | 短路与运算 | 结果 |
---|---|---|---|
true | true | cmd1 && cmd2 | true |
true | true | cmd1 && cmd2 | true |
true | cmd1 && cmd2 | true | |
true | cmd1 && cmd2 | true |
当cmd1的结果为false时,整个表达式就是false,cmd2不参与运算,这就是短路运算
短路或 ||
cmd1 | cmd2 | 运算 | 结果 |
---|---|---|---|
true | cmd1 || cmd2 | ||
true | cmd1 || cmd2 | ||
false | true | cmd1 || cmd2 | true |
false | false | cmd1 || cmd2 | false |
当cmd1 的结果为true时,整个表达式就是 true, cmd2 不参与运算,这就是所谓的短路
短路与和或组合
cmd1 | cmd2 | cmd3 | 运算 | 结果 |
---|---|---|---|---|
true | true | true | cmd1 && cmd2 ||cmd3 | true |
true | true | false | cmd1 && cmd2 ||cmd3 | true |
true | false | true | cmd1 && cmd2 ||cmd3 | true |
true | false | false | cmd1 && cmd2 ||cmd3 | false |
false | true | true | cmd1 && cmd2 ||cmd3 | true |
false | true | false | cmd1 && cmd2 ||cmd3 | false |
false | false | true | cmd1 && cmd2 ||cmd3 | true |
false | false | false | cmd1 && cmd2 ||cmd3 | false |
当cmd1 的结果为true时,会执行 cmd2;
当cmd1 的结果为false时,会执行 cmd3;
cmd1 || cmd2 && cmd3 这种语法是错误的,不使用;
与 和 短路与 的结果是一样的 只有当cmd1为真才执行cmd2
或 和 短路或 的结果是一样的 只有当cmd1为假才执行cmd2
#1.查看vgdisplay pvdisplay
#2.把某个要拆除的pv的数据迁移走
pvmove name
#3.再出查看 pvdisplay
#4.在vg中拆除pv
vgreduce name(vg) name(pv)
#5.查看pvs
#6.删除pvremove