《从入门到精通:Shell脚本开发指南》

目录

一、shell脚本基本语法

2.shell脚本创建过程

3.shell脚本注释规范

4.执行shell脚本

5.shell脚本调试

二、变量

1.变量类型

2.shell中变量命名规则

3.变量定义和引用

3.1变量赋值

3.2变量引用

3.3显示已定义所有变量

3.4删除变量

4.环境变量

5.只读变量

6.位置变量

7.退出状态码变量

8.脚本安全

三、格式化输出print

四、展开命令行

五、算术运算

六、逻辑运算

1.与

2.或

3.非

4.亦或

5.短路运算

5.1短路与

5.2短路或

5.3短路与或组合

七、条件测试命令

1.变量测试

2.数值测试

3.字符串测试

4.文件测试

5.关于 ( ) 和 { } 

6.组合测试条件

6.1第一种方式

6.2第二种方式

八、使用read命令来接受输入

九、bash shell配置文件

1.生效范围

2.shell登录两种方式

3.功能划分

4.编辑配置文件生效方法

5.Bash退出任务

十、流程控制

1.条件选择

1.1选择执行if语句

1.2条件判断case语句

2.循环

2.1.for

2.2.while

2.3.until

2.4.循环控制语句 continue

2.5循环控制 shift 命令

2.6循环与菜单 select

3.函数

3.1定义函数

3.2查看函数

3.3删除函数

3.4函数调用

3.5使用函数文件

3.6函数返回值

3.7环境函数

3.8函数参数

3.9函数变量

4.递归

十一、其他脚本相关工具

1.信号捕捉trap

2.创建临时文件mktemp

3.安装复制文件install

十二、数组

1.声明数组

2.数组赋值

3.显示所有数组

4.引用数组

4.1引用特定的数组元素

4.2引用数组所有元素

4.3数组的长度

4.4数组的所有下标

5.删除数组

5.1删除数组中单个元素

5.2删除整个数组

6.数组数据处理

6.1数组切片

6.2向数组中追加元素

7.关联数组

十三、字符串处理

1.字符串切片

1.1基于偏移量取字符串

1.2基于模式取子串

1.3查找替换

1.4查找并删除

1.5字符大小写转换

1.6变量扩展

十四、高级变量

1.高级变量赋值

2.高级变量用法-有类型变量

3.变量间接引用

3.1eval

3.2间接变量引用


一、shell脚本基本语法

shell是基于过程式、解释执行的语言。shell脚本是包含一些命令或声明,并符合一定格式的文本文件。

一个shell脚本文件中主要包含以下内容:
        各种系统命令的组合
        数据存储:变量、数组
        表达式:a + b 
        控制语句:if

格式要求:首行shebang机制

#!/bin/bash 
#!/usr/bin/python
#!/usr/bin/perl 
#!/usr/bin/ruby
#!/usr/bin/lua

2.shell脚本创建过程

过程:

① 用编辑器创建新文件,首行必须是 shell 声明(shebang),编辑完成后保存退出
② 添加可执行权限 chmod a+x name.sh
③ 运行脚本 

3.shell脚本注释规范

生产环境中,一般要注明作者,创建(修改)时间,文件名,版本号,该程序的功能及作用,目地,版权信息,作者联系方式等。

#配置vim 自动生成注释

" 基础设置
set nu           " 显示行号
set ts=4         " Tab 宽度为 4
set expandtab    " 将 Tab 转换为空格

" 定义脚本标题模板函数
func SetShellTitle()
    if expand("%:e") == 'sh'  " 仅对 .sh 文件生效
        call setline(1, "#!/bin/bash")
        call setline(2, "#")
        call setline(3, "#====================================================")
        call setline(4, "# Author:      Your Name")
        call setline(5, "# Email:       your@email.com")
        call setline(6, "# Date:        ".strftime("%Y-%m-%d"))
        call setline(7, "# FileName:    ".expand("%"))
        call setline(8, "# Description: ")
        call setline(9, "# Version:     1.0")
        call setline(10, "#====================================================")
        call setline(11, "")
        call setline(12, "")
    endif
endfunc

" 自动命令:新建 .sh 文件时调用函数
autocmd BufNewFile *.sh call SetShellTitle()

" 可选:打开 .sh 文件时自动定位到最后一行(方便编辑)
autocmd BufNewFile *.sh execute "normal G"

4.执行shell脚本

root@ubuntu2204 ~]# bash hello.sh 
hello, world

#管道
root@ubuntu2204 ~]# cat hello.sh | bash
hello, world

#重定向
root@ubuntu2204 ~]# bash < hello.sh 
hello, world

#重定向
root@ubuntu2204 ~]# bash <<< $(cat hello.sh)
hello, world

root@ubuntu2204 ~]# chmod +x hello.sh 
#绝对路径
root@ubuntu2204 ~]# /root/hello.sh 
hello, world
Hello, world!
#相对路径
root@ubuntu2204 ~]# ./hello.sh 
hello, world
Hello, world!

#当前进程中执行
root@ubuntu2204 ~]# . hello.sh 
hello, world

root@ubuntu2204 ~]#source hello.sh 
hello, world

#子进程中执行
root@ubuntu2204 ~]#. /hello.sh 
hello, world

root@ubuntu2204 ~]#bash hello.sh 
hello, world
#本地执行远程脚本
root@ubuntu2204 ~]# curl http://10.0.0.157/test.sh -s | bash
hello, world

root@ubuntu2204 ~]# curl http://10.0.0.157/test.sh 2>/dev/null | bash
hello, world

root@ubuntu2204 ~]# wget -qO - http://10.0.0.157/test.sh | bash
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

[root@ubuntu-140 923]#cat test.sh
#!/bin/bash

var1=111

echo $var1
echo $BASHPID
sleep 50

#当前进程 
[root@ubuntu-140 923]#echo $BASHPID
2509

[root@ubuntu-140 923]#vim test.sh
[root@ubuntu-140 923]#. test.sh
111
222

[root@ubuntu-140 923]#echo $var1
111

[root@ubuntu-140 923]#pstree -p | grep 2509
           |           `-sshd(2410)---bash(2509)---sleep(3042)


#子进程
[root@ubuntu-140 923]#echo $BASHPID
2509

[root@ubuntu-140 923]#./test.sh
111
3056

#子进程中的变量己经销毁
[root@ubuntu-140 923]# echo $VAR1

5.shell脚本调试

执行前先做语法检查

此项:语法检查
读取脚本但不执行,仅检查语法错误,不能检测命令错误;如果脚本有语法错误,Bash 会报错;若无错误,则无输出(静默检查)。

bash -n test.sh

此项:调试模式
逐行输出命令,并输出执行结果

bash -x test.sh

总结:脚本错误常见的有三种
语法错误:会导致后续的命令不继续执行,可以用 bash -n 检查错误,提示的出错行数不一定是准确的。
命令错误:默认后续的命令还会继续执行,用 bash -n 无法检查出来 ,可以使用 bash -x 进行观察。
逻辑错误:只能使用 bash -x 进行观察。

二、变量

变量表示命名的内存空间,将数据放在内存空间中,通过变量名引用,获取数据。数据要存在内存空间中,而内存空间又是通过内存地址来访问的,但内存地址一般是16进制的编号;为了方便使用,就有了变量名,当访问一个变量时,实际是访问其对应的内存地址的那块内存空间。

1.变量类型

变量类型:
内置变量,如:PS1,PATH,UID,HOSTNAME,BASHPID,$?,HISTSIZE
自定义变量

不同的变量存放的数据不同,决定了数据存储方式、参与的运算、表示的数据范围不同。


静态和动态语言

静态编译语言:使用变量前,先声明变量类型,之后类型不能改变,在编译时检查。
动态编译语言:不用事先声明,可随时改变类型。

强类型和弱类型语言

强类型语言:不同类型数据操作,必须经过强制转换成同一类型才能运算。
弱类型语言:语言的运行时会隐式做数据类型转换。无须指定类型,默认均为字符型;参与运算会自动进行隐式类型转换;变量无须事先定义可直接调用。


2.shell中变量命名规则

总结:

Linux Shell变量命名需遵循:区分大小写、避开保留字、仅含字母/数字/下划线且不以数字开头,推荐用有意义的英文单词,可采用大写(全局)、小写(局部)、驼峰或下划线命名法。

3.变量定义和引用

普通变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效。
环境变量:生效范围为当前shell进程及其子进程。
本地变量:生效范围为当前shell进程中某代码片断,通常指函数。

3.1变量赋值

name='value'    #直接字串

name="$USER"    #变量引用
name=`COMMAND`  #命令引用
name=$(COMMAND) #命令引用

注意:变量赋值是临时生效,当退出终端后,变量会自动删除,无法持久保存,脚本中的变量会随着脚本结束,也会自动删除。

3.2变量引用

$name
${name}

弱引用和强引用

"$name" 弱引用,其中的变量引用会被替换为变量值
'$name' 强引用,其中的变量引用不会被替换为变量值,而保持原字符串

#变量的各种赋值方式和引用

[root@ubuntu2204 ~]# TITLE='cto'

[root@ubuntu2204 ~]# echo $TITLE
cto

[root@ubuntu2204 ~]# echo I am $TITLE
I am cto

[root@ubuntu2204 ~]# echo "I am $TITLE"
I am cto

[root@ubuntu2204 ~]# echo 'I am $TITLE'
I am $TITLE

[root@ubuntu2204 ~]# NAME=$USER

[root@ubuntu2204 ~]# echo $NAME
root

[root@ubuntu2204 ~]# USER=`whoami`

[root@ubuntu2204 ~]# echo $USER
root

[root@ubuntu2204 ~]# USER=$(whoami)

[root@ubuntu2204 ~]# echo $USER
root

[root@ubuntu-140 923]#ID=`id`
[root@ubuntu-140 923]#echo ID
ID
[root@ubuntu-140 923]#echo $ID
uid=0(root) gid=0(root) groups=0(root)
[root@ubuntu-140 923]#echo ${ID}
uid=0(root) gid=0(root) groups=0(root)
#保留格式

[root@ubuntu2204 ~]# seq 5
1
2
3
4
5

[root@ubuntu2204 ~]# NUM=`seq 5`

[root@ubuntu2204 ~]# echo $NUM
1 2 3 4 5

[root@ubuntu2204 ~]# echo "$NUM"
1
2
3
4
5
#变量引用

[root@ubuntu-140 923]#name=hu
[root@ubuntu-140 923]#age=21
[root@ubuntu-140 923]#echo $name $age
hu 21
[root@ubuntu-140 923]#echo $name$age
hu21
[root@ubuntu-140 923]#echo $name_$age
21
#该问题就是把name_当作变量来处理,无此变量不输出,要加上{}
[root@ubuntu-140 923]#echo ${name}_$age
hu_21
#变量间接复制访问

[root@ubuntu-140 923]#name=hu
[root@ubuntu-140 923]#student=xiaobao

[root@ubuntu-140 923]#echo $student
xiaobao
[root@ubuntu-140 923]#echo $name
hu

[root@ubuntu-140 923]#name=$student
[root@ubuntu-140 923]#echo $name
xiaobao

[root@ubuntu-140 923]#echo $student
xiaobao
#变量附加值

[root@ubuntu-140 923]#title=haohao
[root@ubuntu-140 923]#title+=xuexi
[root@ubuntu-140 923]#echo $title
haohaoxuexi
#变量实现动态命令

[root@ubuntu-140 923]#cmd=hostname
[root@ubuntu-140 923]#echo $cmd
hostname
[root@ubuntu-140 923]#$cmd
ubuntu-140
[root@ubuntu-140 923]#cmd=abc
[root@ubuntu-140 923]#$cmd
Command 'abc' not found, but there are 17 similar ones.

3.3显示已定义所有变量

set 

3.4删除变量

unset <name>
[root@ubuntu-140 923]#name=hu
[root@ubuntu-140 923]#echo $name
hu
[root@ubuntu-140 923]#unset name
[root@ubuntu-140 923]#echo $name

4.环境变量

环境变量是父进程向子进程(及后续子孙进程)传递数据的机制,子进程可继承并修改这些变量(新值会影响孙子进程),但无法反向影响父进程,通常用于系统级配置而非脚本逻辑。

#变量声明和赋值
#声明并赋值
export name=VALUE
declare -x name=VALUE
#或者分两步实现
name=VALUE
export name

#变量引用
$name
${name}

#显示所有环境变量
env
printenv
export
declare -x

#查看指定进程的环境变量
cat /proc/$PID/environ

#删除变量
unset <name>
[root@ubuntu-140 923]#cat test.sh
#!/bin/bash

echo $var1
echo $var2
echo $PATH
echo $PS1

[root@ubuntu-140 923]#var1=120;export var2=xiaobao
[root@ubuntu-140 923]#./test.sh

xiaobao
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

[root@ubuntu-140 923]#

5.只读变量

只读变量:只能声明定义,但后续不能修改和删除,即常量.

#声明只读变量
readonly name
declare -r name

#查看只读变量
readonly [-p]
declare -r
[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

[root@ubuntu2204 ~]# echo $PI
3.14159

[root@ubuntu2204 ~]# exit
logout
Connection closed by foreign host.
Disconnected from remote host(10.0.0.8) at 14:27:04.
Type `help' to learn how to use Xshell prompt.
[c:\~]$ 
Reconnecting in 1 seconds. Press any key to exit local shell.
.
Connecting to 10.0.0.8:22...
Connection established.
To escape to local shell, press 'Ctrl+Alt+]'.
WARNING! The remote SSH server rejected X11 forwarding request.
Last login: Wed Apr  1 13:51:28 2020 from 10.0.0.1
[root@ubuntu2204 ~]# echo $PI

6.位置变量

位置变量:在bash shell中内置的变量, 在脚本代码中调用通过命令行传递给脚本的参数。

$1,$2,...      #对应第1个、第2个等参数,shift [n]换位置
$0             #命令本身,包括路径
$*             #传递给脚本的所有参数,全部参数合为一个字符串
$@             #传递给脚本的所有参数,每个参数为独立字符串
$#             #传递给脚本的参数的个数
               #注意:$@ $* 只在被双引号包起来的时候才会有差异

#清空所有位置变量
set --
[root@ubuntu2204 ~]# cat /data/scripts/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@ubuntu2204 ~]# bash /data/scripts/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

7.退出状态码变量

退出状态码是进程结束时返回给操作系统的数字(0-255),用于表示执行结果(成功/失败)或错误类型,使用变量 $? 保存状态码的相关数字,不同的值反应成功或失败,$?取值范例 0-255。

$?       #0代表成功,1-255代表失败

用户可以在脚本中使用exit [n]命令自定义退出状态码

注意:

脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字。
如果exit后面无数字,终止退出状态取决于exit命令前面命令执行结果;如果没有exit命令,即未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一 条命令的状态码。

8.脚本安全

set 命令:可以用来定制 shell 环境

字符作用
u开启此项,在使用一个没有声明的变量时,会报错,并且终止程序,同 set -o nounset
e开启此项,命令(退出状态码)返回非0就退出,不再继续执行后面的代码,同 set -o errexit
oset -o 显示所有; set -o option 开启 option 项; set +o option 关闭 option 项
xset -x
[root@ubuntu-140 923]#set -o
allexport      	off
braceexpand    	on
emacs          	on
errexit        	off
errtrace       	off
functrace      	off
hashall        	on
histexpand     	on
history        	on
ignoreeof      	off
interactive-comments	on
keyword        	off
monitor        	on
noclobber      	off
noexec         	off
noglob         	off
nolog          	off
notify         	off
nounset        	off
onecmd         	off
physical       	off
pipefail       	off
posix          	off
privileged     	off
verbose        	off
vi             	off
xtrace         	off

[root@ubuntu-140 923]# cat set1.sh 
!/bin/bash

set -u
DIR=/test
rm -rf /${DIRR}/*     #DIRR变量不存在,如果没有 set -u 选项,则会删除根目录

[root@ubuntu2204 ~]# bash set1.sh
set2.sh: line 6: DIRR: unbound variable

[root@ubuntu2204 ~]# vim set2.sh 
!/bin/bash

set -e
echo 123
cd /testxx/         #这个目录不存在,如果没有 set -e,则也会删根
rm -rf *
echo 456

#遇到错误行就终止
[root@ubuntu2204 ~]# bash set2.sh
123
set3.sh: line 17: cd: /test2/: No such file or directory

$- 变量

选项备注
h开启hash表缓存外部命令路径
i允许在交互式Shell中使用注释
m开启监控模式,可通过 Job control来控制进程的停止、继续,后台或者前台执行等
b是否支持大括号扩展
h是否可用 ! 快速展开history命令

三、格式化输出print

printf format args...
#常用格式替换符号

%s         字符串
%d,%i      十进制整数
%f         浮点格式
%c         ASCII字符,即显示对应参数的第一个字符
%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 ~]# printf "%s" 1 2 3 4
1234[root@ubuntu2204 ~]#
[root@ubuntu2204 ~]# printf "%s\n" 1 2 3 4
1
2
3
4
[root@ubuntu2204 ~]# printf "%f\n" 1 2 3 4
1.000000
2.000000
3.000000
4.000000

#.2f 表示保留两位小数
[root@ubuntu2204 ~]# printf "%.2f\n" 1 2 3 4
1.00
2.00
3.00
4.00

[root@ubuntu2204 ~]# printf "(%s)" 1 2 3 4;echo 
(1)(2)(3)(4)

[root@ubuntu2204 ~]# printf " (%s) " 1 2 3 4;echo
 (1) (2) (3) (4) 

[root@ubuntu2204 ~]# printf "(%s)\n" 1 2 3 4
(1)
(2)
(3)
(4)

[root@ubuntu2204 ~]# printf "%s %s\n" 1 2 3 4
1 2
3 4

[root@ubuntu2204 ~]# printf "%s %s %s\n" 1 2 3 4
1 2 3
4  

#%-10s 表示宽度10个字符,左对齐
[root@ubuntu2204 ~]# printf "%-10s %-10s %-4s %s \n" 姓名 性别 年龄 体重 小明 男 20 70 小红 女 18 50
姓名     性别     年龄   体重
小明     男        20   70
小红     女        18   50

#将十进制的17转换成16进制数
[root@ubuntu2204 ~]# printf "%X" 17
11[root@ubuntu2204 ~]#

#将十六进制转换成十进制
[root@ubuntu2204 ~]# printf "%d\n" 0xC
12
[root@ubuntu2204 ~]# VAR="welcome to linux";printf "\033[1;32m%s\033[0m " $VAR
welcome to linux
[root@ubuntu2204 ~]# VAR="welcome to linux";printf "\033[1;32m%s\033[0m\n" $VAR
welcome to linux
[root@ubuntu2204 ~]# VAR="welcome to linux";printf "\033[1;32m%s\033[0m\n" "$VAR"
welcome to linux

四、展开命令行

        在Shell中执行命令时,输入的命令行会按严格顺序分步处理:首先拆分为初始命令词,随后依次展开别名、大括号扩展、波浪符~、命令替换$( )和` `(此时可能因展开内容改变结构而需重新拆分命令词),接着展开变量(如$USER)和文件通配符(如*.txt),最后处理I/O重定向(如>),最终将完全展开后的命令参数传递给实际程序执行

防止扩展

\    #使字符按原意解释
[root@ubuntu2204 ~]# echo Your cost: \$5.00 
Your cost: $5.00

[root@ubuntu2204 ~]# echo "The book's price is \$10"
The book's price is $10

加引号来防止扩展

' '     #单引号 防止所有扩展
" "     #双引号 也可防止扩展,但是以下情况例外 $

变量扩展

``     #反引号,命令替换
\      #反斜线,禁止单个字符扩展
!      #叹号,历史命令替换

五、算术运算

+ -                #addition, subtraction
* / %              #multiplication, division, remainder, %表示取模,即取余数
i++ i--            #variable post-increment and post-decrement
++i --i            #variable pre-increment and pre-decrement
= *= /= %= += -= <<= >>= &= ^= |=   #assignment
- +                #unary minus and plus
! ~                #logical and bitwise negation
**                 #exponentiation 乘方,即指数运算
<< >>              #left and right bitwise shifts
<= >= < >          #comparison
== !=              #equality and inequality
&                  #bitwise AND
|                  #bitwise OR
^                  #bitwise exclusive OR
&&                 #logical AND
||                 #logical OR
expr?expr:expr     #conditional operator
expr1 , expr2      #comma

注意:bash 只支持整数,不支持小数。

#不转义,无法进行算术运算

[root@ubuntu2204 ~]# i=123;j=456;
[root@ubuntu2204 ~]# k=i+j;echo $k
i+j
[root@ubuntu2204 ~]# k=$i+$j;echo $k
123+456
#实现算术运算

let var=expression

((var=expression))

var=$[expression]

var=$((expression))

var=$(expr arg1 arg2 ...)

declare -i var=number

echo expression|bc
[root@ubuntu2204 ~]# i=123;j=456;
[root@ubuntu2204 ~]# let k=i+j;echo $k
579

[root@ubuntu2204 ~]# ((k=i+j));echo $k
579

[root@ubuntu2204 ~]# k=$[i+j];echo $k
579

[root@ubuntu-140 923]#k=$((i+j));echo $k
579

[root@ubuntu-140 923]#k=$(expr $i+$j);echo $k
579

[root@ubuntu-140 923]#declare -i k=i+j;echo $k
579

[root@ubuntu-140 923]#echo $i + $j | bc
579
#随机数生成器变量

$RANDOM   #取值范围:0-32767

#生成0-50随机数
[root@ubuntu-140 923]#echo $[$RANDOM%50]
46
[root@ubuntu-140 923]#echo $[$RANDOM%50]
37
[root@ubuntu-140 923]#echo $[$RANDOM%50]
25

#随机字体颜色
echo -e "\033[1;$[RANDOM%7+31]mhello\033[0m"
#增强型赋值

+=         # i+=10 相当于 i=i+10
-=         # i-=j 相当于 i=i-j
*=
/=
%=
++         # i++,++i   相当于 i=i+1
--         # i--,--i   相当于 i=i-1
[root@ubuntu2204 ~]# i=1;let j=i++;echo $i $j     
2 1
[root@ubuntu2204 ~]# i=1;let j=++i;echo $i $j
2 2

[root@ubuntu2204 ~]# let i=10*2
[root@ubuntu2204 ~]# echo $i
20
[root@ubuntu2204 ~]# ((j=i+10))
[root@ubuntu2204 ~]# echo $j
30
[root@ubuntu2204 ~]# i=10;((i++));echo $i 
11

[root@ubuntu2204 ~]# i=10
[root@ubuntu2204 ~]# let i+=20 
[root@ubuntu2204 ~]# echo $i
30
[root@ubuntu2204 ~]# j=20
[root@ubuntu2204 ~]# let i*=j 
[root@ubuntu2204 ~]# echo $i
600

[root@ubuntu2204 ~]# unset i j ; i=1; let j=i++; echo "i=$i,j=$j"
i=2,j=1
[root@ubuntu2204 ~]# unset i j ; i=1; let j=++i; echo "i=$i,j=$j"
i=2,j=2

六、逻辑运算

bool值二进制表示说明
true1
false0

1.与

&:有0则0,同1则1

var1var2与运算结果
000&00
010&10
101&00
111&11

2.或

|:有1则1,同0则0

var1var2或运算结果
000|00
010|11
101|01
111|10

3.非

!:1变0,0变1

var非运算结果
1!10
0!01

4.亦或

^:相同为0,相异为1

说明:两个数字XY异或得到结果ZZ再和任意两者之一X异或,将得出另一个值Y。

var1var2亦或运算结果
000^00
010^11
101^01
111^10
#变量互换
[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

5.短路运算

5.1短路与

        当cmd1 的结果为false时,整个表达式就是 false, cmd2 不参与运算,这就是所谓的短路。

cmd1cmd2短路与运算结果
truetrue

cmd1&&cmd2

true
truefalsecmd1&&cmd2false
falsecmd1&&cmd2false
falsecmd1&&cmd2false

5.2短路或

        当cmd1 的结果为true时,整个表达式就是 true, cmd2 不参与运算,这就是所谓的短路。

cmd1cmd2短路或运算结果
true

cmd1||cmd2

true
truecmd1||cmd2true
falsetruecmd1||cmd2true
falsefalsecmd1||cmd2false

5.3短路与或组合

        当cmd1 的结果为true时,会执行 cmd2;当cmd1 的结果为false时,会执行 cmd3;cmd1 || cmd2 && cmd3 这种语法是错误的,不使用。

cmd1cmd2cm3组合运算结果
truetruetrue
cmd1 && cmd2 || cmd3
true
truetruefalse
cmd1 && cmd2 || cmd3
true
truetruetrue
cmd1 && cmd2 || cmd3
true
truefalsefalse
cmd1 && cmd2 || cmd3
false
falsetruetrue
cmd1 && cmd2 || cmd3
true
falsetruefalse
cmd1 && cmd2 || cmd3
false
falsefalsetrue
cmd1 && cmd2 || cmd3
true
falsefalsefalse
cmd1 && cmd2 || cmd3
false

七、条件测试命令

#条件测试命令
#注意:[]中的表达式,前后要有空格

test expr
[ arg... ] #同 test
[[ expression ]] #增加版的[],支持[],支持扩展正则表达式和通配符
#其他

-o OPTION      #如果在shell 中开启了某项,则为真
-v VAR         #如果变量被设置为为真
-R VAR         #如果变量被设置且被引用则为真
! EXPR         #表达式结果取反
EXPR1 -a EXPR2     #如果 expr1 和 expr2 都为真则为真
EXPR1 -o EXPR2     #如果 expr1 和 expr2 有一个为真则为真

1.变量测试

[root@ubuntu2204 ~]# test -v x 
[root@ubuntu2204 ~]# echo $?
1

[root@ubuntu2204 ~]# x=10
[root@ubuntu2204 ~]# test -v x 
[root@ubuntu2204 ~]# echo $?
0

[root@ubuntu2204 ~]# y=
[root@ubuntu2204 ~]# test -v y
[root@ubuntu2204 ~]# echo $?
0

#注意 [ ] 中需要空格,否则会报下面错误
[root@ubuntu2204 ~]# [-v name]
-bash: [-v: command not found
[root@ubuntu2204 ~]# [ -v name ]
[root@ubuntu2204 ~]# echo $?
0

2.数值测试

#数学相关

#数字相关 格式 arg1 OP arg2
-eq     #等于
-ne     #不等于
-lt     #小于
-le     #小于等于
-gt     #大于
-ge     #大于等于

#算术表达式比较
==     #相等
!=     #不相等
<=     #小于或等于
>=     #大于或等于
<      #小于
>      #大于
[root@ubuntu2204 ~]# i=10
[root@ubuntu2204 ~]# j=8
[root@ubuntu2204 ~]# [ $i -lt $j ] 
[root@ubuntu2204 ~]# echo $?
1

[root@ubuntu2204 ~]# [ $i -gt $j ] 
[root@ubuntu2204 ~]# echo $?
0

[root@ubuntu2204 ~]# [ i -gt j ] 
-bash: [: i: integer expression expected

#算术表达式比较
[root@ubuntu2204 ~]# x=10;y=10;(( x == y ));echo $?
0
[root@ubuntu2204 ~]# x=10;y=20;(( x == y ));echo $?
1
[root@ubuntu2204 ~]# x=10;y=20;(( x != y ));echo $?
0
[root@ubuntu2204 ~]# x=10;y=10;(( x != y ));echo $?
1
[root@ubuntu2204 ~]# x=10;y=20;(( x > y ));echo $?
1
[root@ubuntu2204 ~]# x=10;y=20;(( x < y ));echo $?
0

3.字符串测试

test和[ ]字符串测试用法

#字符串判断相关

-z STRING     #判断字符串是否为空,为空则为真
-n STRING     #如果字符串不为空则为真
STRING        #同上

#两个字符串变量比较,一定要有空格,如果没有空格,就变成赋值了
STRING1 = STRING2      #如果 string1 和 string2 字符串相同则为真
STRING1 != STRING2     #如果 string1 和 string2 字符串不相同则为真
STRING1 < STRING2   #只比较第一个字符,以字母表顺序来比较,string1在string2 之前则为真,< 要转义
STRING1 > STRING2   #只比较第一个字符,以字母表顺序来比较,string1在string2 之后则为真, > 要转义
#字符串不为空

#空
[root@ubuntu2204 ~]# [ -n "" ];echo $?
1

#这种是不为空
[root@ubuntu2204 ~]# [ -n " " ];echo $?
0

#变量得加引号
[root@ubuntu2204 ~]# [ -n $xx ];echo $?
0
[root@ubuntu2204 ~]# [ -n "$xx" ];echo $?
1
————————————————————————————————————————————————————————————————————————————————————————
[root@ubuntu2204 ~]# unset str
[root@ubuntu2204 ~]# [ -z "$str" ]
[root@ubuntu2204 ~]# echo $?
0

[root@ubuntu2204 ~]# str=""
[root@ubuntu2204 ~]# [ -z "$str" ]
[root@ubuntu2204 ~]# echo $?
0

[root@ubuntu2204 ~]# str=" "
[root@ubuntu2204 ~]# [ -z "$str" ]
[root@ubuntu2204 ~]# echo $?
1

[root@ubuntu2204 ~]# [ -n "$str" ]
[root@ubuntu2204 ~]# echo $?
0

[root@ubuntu2204 ~]# unset str
[root@ubuntu2204 ~]# [ -n "$str" ]
[root@ubuntu2204 ~]# echo $?
1

[root@ubuntu2204 ~]# [ "$str" ]
[root@ubuntu2204 ~]# echo $?
1

[root@ubuntu2204 ~]# str1=abc
[root@ubuntu2204 ~]# [ "$str" ]
[root@ubuntu2204 ~]# echo $?
0

[root@ubuntu2204 ~]# str2=def
[root@ubuntu2204 ~]# [ "$str" ]
[root@ubuntu2204 ~]# echo $?
0

[root@ubuntu2204 ~]# str1=abc
[root@ubuntu2204 ~]# str2=def
[root@ubuntu2204 ~]# [ "$str1" = "$str2" ]
[root@ubuntu2204 ~]# echo $?
1

[root@ubuntu2204 ~]# str2=abc
[root@ubuntu2204 ~]# [ "$str1" = "$str2" ]
[root@ubuntu2204 ~]# echo $?
0
————————————————————————————————————————————————————————————————————————————————————————
#在比较字符串时,建议变量放在" "中

[root@ubuntu-140 924]#NAME="I AM A BOY"
[root@ubuntu-140 924]#echo $NAME
I AM A BOY
[root@ubuntu-140 924]#[ $NAME ]
-bash: [: too many arguments
[root@ubuntu-140 924]#[ "$NAME" ]
[root@ubuntu-140 924]#echo $?
0
[root@ubuntu-140 924]#[ I AM A BOY ];echo $?
-bash: [: too many arguments
2
[root@ubuntu-140 924]#[ "I AM A BOY" ];echo $?
0
————————————————————————————————————————————————————————————————————————————————————————
#比较符要转义
#这个写法有问题
[root@ubuntu2204 ~]# a=a;b=b; [ $a > $b ]; echo $?
0
#这个才是正确的
[root@ubuntu2204 ~]# a=a;b=b; [ "$a" \> "$b" ]; echo $?
1
[root@ubuntu2204 ~]# a=b;b=a; [ "$a" \> "$b" ]; echo $?
0
————————————————————————————————————————————————————————————————————————————————————————


[[ ]]字符串测试用法

[[ expression ]] 用法

==         #左侧字符串是否和右侧的PATTERN相同
           #注意:此表达式用于[[ ]]中,PATTERN为通配符
   
=~         #左侧字符串是否能够被右侧的正则表达式的PATTERN所匹配
           #注意: 此表达式用于[[ ]]中为扩展的正则表达式

注意:当使用正则表达式或通配符使用[[ ]],其它情况一般使用 [ ]。

[root@ubuntu2204 ~]# FILE="a*"
[root@ubuntu2204 ~]# echo $FILE
a*

[root@ubuntu2204 ~]# [[ $FILE == a* ]]
[root@ubuntu2204 ~]# echo $?
0

[root@ubuntu2204 ~]# FILE="ab"
[root@ubuntu2204 ~]# [[ $FILE == a* ]]
[root@ubuntu2204 ~]# echo $?
0

[root@ubuntu2204 ~]# FILE="a*"
#[[]]中如果不想使用通配符*,只想表达*本身,可以用" "引起来
[root@ubuntu2204 ~]# [[ $FILE == a"*" ]]
[root@ubuntu2204 ~]# echo $?
0

[root@ubuntu2204 ~]# FILE="ab"
[root@ubuntu2204 ~]# [[ $FILE == a"*" ]]
[root@ubuntu2204 ~]# echo $?
1

#[[]]中如果不想使用通配符*,只想表达*本身,也可以使用转义符
[root@ubuntu2204 ~]# [[ $FILE == a\* ]]
[root@ubuntu2204 ~]# echo $?
1

[root@ubuntu2204 ~]# FILE="a\b"
[root@ubuntu2204 ~]# [[ $FILE == a\* ]]
[root@ubuntu2204 ~]# echo $?
1

[root@ubuntu2204 ~]# FILE="a*"
[root@ubuntu2204 ~]# [[ $FILE == a\* ]]
[root@ubuntu2204 ~]# echo $?
0

#通配符?
[root@ubuntu2204 ~]# FILE=abc
[root@ubuntu2204 ~]# [[ $FILE == ??? ]]
[root@ubuntu2204 ~]# echo $?
0

[root@ubuntu2204 ~]# FILE=abcd
[root@ubuntu2204 ~]# [[ $FILE == ??? ]]
[root@ubuntu2204 ~]# echo $?
1

#通配符
[root@ubuntu2204 ~]# NAME="linux1"
[root@ubuntu2204 ~]# [[ "$NAME" == linux* ]]
[root@ubuntu2204 ~]# echo $?
0

[root@ubuntu2204 ~]# [[ "$NAME" == "linux*" ]]
[root@ubuntu2204 ~]# echo $?
1

[root@ubuntu2204 ~]# NAME="linux*"
[root@ubuntu2204 ~]# [[ "$NAME" == "linux*" ]]
[root@ubuntu2204 ~]# echo $?
0

#结论:[[ == ]] == 右侧的 * 做为通配符,不要加"",只想做为*符号使用时, 需要加 "" 或转义\
————————————————————————————————————————————————————————————————————————————————————————
#正则

[root@ubuntu2204 ~]# SCORE=101
[root@ubuntu2204 ~]# [[ $SCORE =~ 100|[0-9]{1,2} ]]
[root@ubuntu2204 ~]# echo $?
0
[root@ubuntu2204 ~]# [[ $SCORE =~ ^(100|[0-9]{1,2})$ ]]
[root@ubuntu2204 ~]# echo $?
1
[root@ubuntu2204 ~]# SCORE=10
[root@ubuntu2204 ~]# [[ $SCORE =~ ^(100|[0-9]{1,2})$ ]]
[root@ubuntu2204 ~]# echo $?
0
[root@ubuntu2204 ~]# SCORE=abc
[root@ubuntu2204 ~]# [[ $SCORE =~ ^(100|[0-9]{1,2})$ ]]
[root@ubuntu2204 ~]# echo $?
1
————————————————————————————————————————————————————————————————————————————————————————
#通配符
[root@ubuntu2204 ~]# FILE=test.log
[root@ubuntu2204 ~]# [[ "$FILE" == *.log ]]
[root@ubuntu2204 ~]# echo $?
0
[root@ubuntu2204 ~]# FILE=test.txt
[root@ubuntu2204 ~]# [[ "$FILE" == *.log ]]
[root@ubuntu2204 ~]# echo $?
1
[root@ubuntu2204 ~]# [[ "$FILE" != *.log ]]
[root@ubuntu2204 ~]# echo $?
0
#正则表达式
[root@ubuntu2204 ~]# [[ "$FILE" =~ \.log$ ]]
[root@ubuntu2204 ~]# echo $?
1
[root@ubuntu2204 ~]# FILE=test.log
[root@ubuntu2204 ~]# [[ "$FILE" =~ \.log$ ]]
[root@ubuntu2204 ~]# echo $?
0
————————————————————————————————————————————————————————————————————————————————————————
#判断合法的非负整数

[root@ubuntu2204 ~]# N=100
[root@ubuntu2204 ~]# [[ "$N" =~ ^[0-9]+$ ]]
[root@ubuntu2204 ~]# echo $?
0
[root@ubuntu2204 ~]# N=huhao103
[root@ubuntu2204 ~]# [[ "$N" =~ ^[0-9]+$ ]]
[root@ubuntu2204 ~]# echo $?
1
————————————————————————————————————————————————————————————————————————————————————————
#判断合法IP

[root@ubuntu2204 ~]# IP=1.2.3.4
[root@ubuntu2204 ~]# [[ "$IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]
[root@ubuntu2204 ~]# echo $?
0
[root@ubuntu2204 ~]# IP=1.2.3.4567
[root@ubuntu2204 ~]# [[ "$IP" =~ ^([0-9]{1,3}.){3}[0-9]{1,3}$ ]]
[root@ubuntu2204 ~]# echo $?
1
[root@ubuntu2204 ~]# [[ $IP =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-4])$ ]]
[root@ubuntu2204 ~]# echo $?
1

4.文件测试

#文件判断相关
-a FILE         #如果文件存在则为真
-b FILE         #如果文件为块特殊文件则为真
-c FILE         #如果文件为字符特殊文件则为真
-d FILE         #如果文件为目录则为真
-e FILE         #如果文件存在则为真
-f FILE         #如果文件存在且为常规文件则为真
-g FILE         #如果文件的组属性设置打开则为真
-h FILE         #如果文件为符号链接则为真
-L FILE         #如果文件为符号链接则为真
-k FILE         #如果文件的粘滞 (sticky) 位设定则为真
-p FILE         #如果文件为命名管道则为真
-r FILE         #如果当前用户对文件有可读权限为真
-s FILE         #如果文件存在且不为空则为真
-S FILE         #如果文件是套接字则为真
-t FD           #如果文件描述符在一个终端上打开则为真
-u FILE         #如果文件设置了SUID特殊权限则为真
-w FILE         #如果当前用户对文件有可写权限为真
-x FILE         #如果当前用户对文件有可执行权限为真
-O FILE         #如果当前用户是文件属主为真
-G FILE         #如果当前用户对文件属组为真
-N FILE         #如果文件上次被读取之后修改过则为真
FILE1 -nt FILE2     #如果 file1 文件新于 file2 文件则为真(根据修改日期)
FILE1 -ot FILE2     #如果 file1 文件旧于 file2 文件则为真
FILE1 -ef FILE2     #如果 file1 文件是 file2 文件的硬链接则为真
[root@ubuntu2204 ~]# [ -a /etc/nologin ] 
[root@ubuntu2204 ~]# echo $?
1

[root@ubuntu2204 ~]# ! [ -a /etc/nologin ] 
[root@ubuntu2204 ~]# echo $?
0

#文件是否存在
[root@ubuntu2204 ~]# [ -a /etc/issue ]
[root@ubuntu2204 ~]# echo $?
0

#取反后结果却没有变化
[root@ubuntu2204 ~]# [ ! -a /etc/issue ]
[root@ubuntu2204 ~]# echo $?
0

[root@ubuntu2204 ~]#! [ -a /etc/issue ]
[root@ubuntu2204 ~]# echo $?
1

#文件是否存在
[root@ubuntu2204 ~]#! [ -e /etc/issue ]
[root@ubuntu2204 ~]# echo $?
1

[root@ubuntu2204 ~]# [ ! -e /etc/issue ]
[root@ubuntu2204 ~]# echo $?
1

[root@ubuntu2204 ~]# [ -d /etc ]
[root@ubuntu2204 ~]# echo $?
0

[root@ubuntu2204 ~]# [ -d /etc/issue ]
[root@ubuntu2204 ~]# echo $?
1

[root@ubuntu2204 ~]# [ -L /bin ]
[root@ubuntu2204 ~]# echo $?
0

[root@ubuntu2204 ~]# [ -L /bin/ ]
[root@ubuntu2204 ~]# echo $?

#文件权限测试
#注意:最终结果由用户对文件的实际权限决定,而非文件属性决定
[root@ubuntu2204 ~]# [ -w /etc/shadow ] 
[root@ubuntu2204 ~]# echo $?
0
[root@ubuntu2204 ~]# [ -x /etc/shadow ] 
[root@ubuntu2204 ~]# echo $?
1
[root@ubuntu2204 ~]# [ -w test.txt ]
[root@ubuntu2204 ~]# echo $?
0
[root@ubuntu2204 ~]# chattr +i test.txt 
[root@ubuntu2204 ~]# lsattr test.txt
----i---------e------- test.txt
[root@ubuntu2204 ~]# [ -w test.txt ]
[root@ubuntu2204 ~]# echo $?
1
[root@ubuntu2204 ~]# chattr -i test.txt 
[root@ubuntu2204 ~]# [ -w test.txt ]
[root@ubuntu2204 ~]# echo $?
0

#文件属性测试
[root@ubuntu2204 ~]# [ -s /etc/passwd ];echo $?
0
[root@ubuntu2204 ~]# [ -s nofile ];echo $?
1
[root@ubuntu2204 ~]# [ -O /etc/passwd ];echo $?
0

5.关于 ( ) 和 { } 

( cmd1;cmd2;... ) 和 { cmd1;cmd2;...; } 都可以将多个命令组合在一起,批量执行

( list ) 会开启子shell,并且list中变量赋值及内部命令执行后,将不再影响后续的环境

{ list; } 不会开启子shell, 在当前shell中运行,会影响当前shell环境,左侧要有空格,右侧要有; 结束
[root@ubuntu2204 ~]# name=aaa;(echo $name;name=bbb;echo $name );echo $name
aaa
bbb
aaa
[root@ubuntu2204 ~]# name=aaa;{ echo $name;name=bbb;echo $name; } ;echo $name
aaa
bbb
bbb

[root@ubuntu2204 ~]# umask
0022
[root@ubuntu2204 ~]# (umask 066;touch f1.txt)
[root@ubuntu2204 ~]# ll f1.txt 
-rw------- 1 root root 0 Dec 23 16:58 f1.txt
[root@ubuntu2204 ~]# umask
0022

[root@ubuntu2204 ~]# ( cd /data;ls )
test.log
[root@ubuntu2204 ~]# pwd
/root
[root@ubuntu2204 ~]# { cd /data;ls; }
test.log
[root@ubuntu2204 ~]# pwd
/data

#()会开启子shell
[root@ubuntu-140 ~]#echo $BASHPID
1433

[root@ubuntu-140 ~]#( echo $BASHPID;sleep 100 )
1443

[root@ubuntu-140 ~]#pstree -p | grep 1443
           |           `-sshd(1376)---bash(1433)---bash(1443)---sleep(1444)

#{ } 不会开启子shell
[root@ubuntu2204 ~]# echo $BASHPID
1433
[root@ubuntu-140 ~]#{ echo $BASHPID; }
1433

6.组合测试条件

6.1第一种方式

[ EXPRESSION1 -a EXPRESSION2 ] #并且,EXPRESSION1和EXPRESSION2都是真,结果才为真
[ EXPRESSION1 -o EXPRESSION2 ] #或者,EXPRESSION1和EXPRESSION2只要有一个真,结果就为真
[ ! EXPRESSION ]               #取反

说明: -a 和 -o 需要使用测试命令进行,[[ ]] 不支持。

[root@ubuntu2204 ~]# FILE="/data/scrips/test.sh"
[root@ubuntu2204 ~]# ll /data/scrips/test.sh
-rw-r--r-- 1 root root 382 Dec 23 09:32 /data/scripts/test.sh

[root@ubuntu2204 ~]# [ -f $FILE -a -x $FILE ] 
[root@ubuntu2204 ~]# echo $?
1

[root@ubuntu2204 ~]# chmod +x /data/scripts/test.sh
[root@ubuntu2204 ~]# ll /data/scripts/test.sh
-rwxr-xr-x 1 root root 382 Dec 23 09:32 /data/script40/test.sh

#并且
[root@ubuntu2204 ~]# [ -f $FILE -a -x $FILE ] 
[root@ubuntu2204 ~]# echo $?
0

[root@ubuntu2204 ~]# chmod -x /data/scripts/test.sh
[root@ubuntu2204 ~]# ll /data/scripts/test.sh
-rw-r--r-- 1 root root 382 Dec 23 09:32 /data/scripts/test.sh

#或者
[root@ubuntu2204 ~]# [ -f $FILE -o -x $FILE ] 
[root@ubuntu2204 ~]# echo $?
0
[root@ubuntu2204 ~]# [ -x $FILE ] 
[root@ubuntu2204 ~]# echo $?
1

#取反
[root@ubuntu2204 ~]# [ ! -x $FILE ] 
[root@ubuntu2204 ~]# echo $?
0
[root@ubuntu2204 ~]# ! [ -x $FILE ] 
0

6.2第二种方式

#条件表达式

#并且,短路与,代表条件性的AND THEN
#如果COMMAND1成功,将执行COMMAND2,否则,将不执行COMMAND2
COMMAND1 && COMMAND2    

#或者,短路或,代表条件性的OR ELSE
#如果COMMAND1 成功,将不执行COMMAND2,否则,将执行COMMAND2
COMMAND1 || COMMAND2 

! COMMAND       #非,取反
[root@ubuntu2204 ~]# test "A" = "B" && echo "Strings are equal" 
[root@ubuntu2204 ~]# test 1 -eq 2 && echo "Integers are equal"
[root@ubuntu2204 ~]# [ "A" = "B" ] && echo "Strings are equal" 
[root@ubuntu2204 ~]# [ "$A" -eq "$B" ] && echo "Integers are equal"
[root@ubuntu2204 ~]# [ -f /bin/cat -a -x /bin/cat ] && cat /etc/fstab
[root@ubuntu2204 ~]# [ -z "$HOSTNAME" -o "$HOSTNAME" = "localhost.localdomain" ] && hostname www.baidu.com

[root@ubuntu2204 ~]# id abc &> /dev/null || useradd abc
[root@ubuntu2204 ~]# getent passwd abc
abc:x:1002:1002::/home/zhang:/bin/bash
[root@ubuntu2204 ~]# grep -q xxx /etc/passwd || echo 'No such user'
[root@ubuntu-140 924]#touch test.sh
[root@ubuntu-140 924]#ll
total 0
-rw-r--r-- 1 root root 0 Sep 24 14:04 test.sh
[root@ubuntu-140 924]#FILE=test.sh

[root@ubuntu-140 924]#[ -f "$FILE" ] && [[ "$FILE"=~.*\.sh$ ]] && chmod a+x "$FILE"

[root@ubuntu-140 924]#ll
total 0
-rwxr-xr-x 1 root root 0 Sep 24 14:04 test.sh

[root@ubuntu-140 924]#IP=192.168.61.136;echo $IP
192.168.61.136
[root@ubuntu-140 924]#ping -c1 -W1 $IP &>/dev/null && echo "$IP is up" || echo "$IP is unreachable"
192.168.61.136 is up


#轮盘游戏
[root@ubuntu2204 ~]# [ $[RANDOM%6] -eq 0 ] && rm -rf /* || echo "Luckly boy"
#条件组合

COMMAND1 && COMMAND2 || COMMAND3 
COMMAND1 || COMMAND2 && COMMAND3  
[root@ubuntu2204 ~]# NAME=hu; id $NAME &> /dev/null && echo "$NAME is exist"
hu is exist

[root@ubuntu2204 ~]# NAME=huge; id $NAME &> /dev/null || echo "$NAME is not exist"
huge is not exist

[root@ubuntu2204 ~]# NAME=wange; id $NAME &> /dev/null && echo "$NAME is exist" || echo "$NAME is not exist"
huge is not exist

[root@ubuntu2204 ~]# NAME=hu; id $NAME &> /dev/null && echo "$NAME is exist" || echo "$NAME is not exist"
hu is exist

[root@ubuntu2204 ~]# NAME=hu; id $NAME &> /dev/null && echo "$NAME is exist" || echo "$NAME is not exist"
hu is exist

[root@ubuntu2204 ~]# NAME=hu; id $NAME &> /dev/null || echo "$NAME is not exist" && echo "$NAME is exist"
wang is exist

[root@ubuntu2204 ~]# NAME=huge; id $NAME &> /dev/null || echo "$NAME is not exist"&& echo "$NAME is exist"
huge is not exist
huge is exist

#结论:如果&&和 || 混合使用,&& 要在前,|| 放在后
[root@ubuntu2204 ~]# NAME=huge; id $NAME &> /dev/null && echo "$NAME is exist" || useradd $NAME
[root@ubuntu2204 ~]# id huge
uid=1002(huge) gid=1002(huge) groups=1002(huge)

八、使用read命令来接受输入

read 是 Bash 中的一个内置命令,用于从标准输入(键盘)或文件中读取一行数据,并将其赋值给一个或多个变量。它是 Shell 脚本中实现用户交互或解析输入的常用工具。

read [options] [name ...]

#常见选项
-p           #指定要显示的提示
-s           #静默输入,一般用于密码
-n N         #指定输入的字符长度N
-d 'CHAR'    #输入结束符
-t N         #TIMEOUT为N秒
[root@ubuntu-140 924]#read
abcdefg
#$REPLY默认接收
[root@ubuntu-140 924]#echo $REPLY
abcdefg

[root@ubuntu-140 924]#read NAME AGE
Hu 22
[root@ubuntu-140 924]#echo $NAME $AGE
Hu 22

[root@ubuntu-140 924]#read -p "Please input your password:" PASSWD
Please input your password:333333
[root@ubuntu-140 924]#echo $PASSWD
333333

#标准输入重定向赋值
[root@ubuntu-140 924]#read x y z <<< "111 222 333"
[root@ubuntu-140 924]#echo $x $y $z
111 222 333

#交互输入参数
  1 #!/bin/bash                                            
  2 
  3 read -p "Please input your name:" NAME
  4 
  5 read -p "Please input your age:" AGE
  6 
  7 echo $NAME $AGE
  8 
  9 
[root@ubuntu-140 924]#chmod a+x io.sh
[root@ubuntu-140 924]#./io.sh
Please input your name:hu
Please input your age:21
hu 21

#管道符
#管道符是开启子进程,所以这样没值
[root@ubuntu-140 924]#echo xiaohu | { read NAME; }
[root@ubuntu-140 924]#echo $NAME

[root@ubuntu-140 924]#echo xiaohu | { read NAME;echo $NAME; }xiaohu

#管道符两边都是子进程
[root@ubuntu-140 924]#echo $BASHPID
1561
[root@ubuntu-140 924]#echo $BASHPID;echo $BASHPID | xargs echo 
1561
2232

[root@ubuntu-140 924]#echo $BASHPID;{ echo $BASHPID; } | xargs echo
1561
2237

[root@ubuntu-140 924]#echo $BASHPID;{ echo $BASHPID; } | { echo $BASHPID;xargs echo; }
1561
2243
2242
#2243右边进程 2242左边进程

[root@ubuntu-140 924]#echo $BASHPID;{ echo $BASHPID; } | { echo $BASHPID;xargs echo;sleep 100; }
1561
2247
2246
           ├─sshd(928)─┬─sshd(1302)───bash(1349)───pstree(2252)
           │           └─sshd(1504)───bash(1561)───bash(2247)───sleep(2250)
#2246因为输出完就结束 
#面试题
[root@ubuntu2204 ~]# cat test.txt
1 2
[root@ubuntu2204 ~]# read i j < test.txt ; echo i=$i j=$j
i=1 j=2
[root@ubuntu2204 ~]# echo 1 2 | read x y ; echo x=$x y=$y
x= y=
[root@ubuntu2204 ~]# echo 1 2 | ( read x y ; echo x=$x y=$y )
x=1 y=2
[root@ubuntu2204 ~]# echo 1 2 | { read x y ; echo x=$x y=$y; }
x=1 y=2
#判断用户输入是否为YES
[root@ubuntu-140 924]#cat a.sh
#!/bin/bash

read -p "Are you rich?(yes/no):" ANSWER

[[ $ANSWER =~ ^([Yy][Ee][Ss]) ]] && echo "You are rich" || echo "Good good study,day day up"

[root@ubuntu-140 924]#./a.sh
Are you rich?(yes/no):no
Good good study,day day up
[root@ubuntu-140 924]#./a.sh
Are you rich?(yes/no):yes
You are rich

#判断用户输入的是否为YES
[root@ubuntu-140 924]#./b.sh
Please input yes or no:YEs
YES
[root@ubuntu-140 924]#./b.sh
Please input yes or no:No
NO
[root@ubuntu-140 924]#cat b.sh
#!/bin/bash

read -p "Please input yes or no:" INPUT

ANSWER=`echo $INPUT | tr 'A-Z' 'a-z'`

[ $ANSWER = 'yes' -o $ANSWER = 'y' ] && echo YES

[ $ANSWER = 'no' -o $ANSWER = 'n' ] && echo NO

#
  1 #!/bin/bash                                                                              
  2 
  3 read -p "Please input yes or no:" INPUT
  4 
  5 [[ $INPUT =~ ^([Yy][Ee][Ss]|[Yy])$ ]] && echo YES
  6 
  7 
  8 [[ $INPUT =~ ^([Nn][Oo]]|[Nn])$ ]] && echo NO
  9 
~     

#检查注意网络状态
read -p "Please input a IP: " IP
[[  "$IP" =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]] || { echo "IP is invalid";exit; }

ping -c1 -W1 $IP &> /dev/null && echo $IP is up || echo $IP is down


10-99
100-199
200-249
250-255       

九、bash shell配置文件

1.生效范围


2.shell登录两种方式

        交互式登录:直接通过终端输入账号密码登录、使用 su - UserName 切换的用户。

        非交互式登录:su UserName 、图形界面下打开的终端、执行脚本、任何其它的bash实例。

3.功能划分

4.编辑配置文件生效方法

        重启shell:退出重新登陆,启动新Bash。

        source命令:即生效,无需重启。

5.Bash退出任务

        保存在 ~/.bash_logout文件中(用户),在退出登录shell时(一般可创建自动备份、清除临时文件)。

十、流程控制

1.条件选择

1.1选择执行if语句

if COMMANDS; then 
    COMMANDS             # 如果 if 后的命令返回 0(成功),执行这里的命令
[ elif COMMANDS; then 
    COMMANDS ]...        # 可选,可以有多个 elif
[ else 
    COMMANDS ]           # 可选,如果所有条件都失败,执行这里的命令
fi

单分支

if 判断条件;then
    条件为真的分支代码
fi
str1=def;
if [ $str1 = "abc" ];then 
    echo "str1 is abc";
fi

双分支

if 判断条件; then
    条件为真的分支代码
else
    条件为假的分支代码
fi
str1=def;
if [ $str1 = "abc" ];then 
    echo "str1 is abc";
else
    echo "str1 is not abc"
fi

多分支

if 判断条件1; then
    条件1为真的分支代码
elif 判断条件2; then
    条件2为真的分支代码
elif 判断条件3; then
    条件3为真的分支代码
...
else
    以上条件都为假的分支代码
fi
str=$1;
if [ "$str" = "abc" ]; then
    echo "is abc";
elif [ "$str" = "def" ]; then
    echo "is def";
elif [ "$str" = "xyz" ]; then
    echo "is xyz";
else
    echo "not abc,not def"
fi

说明:if 语句可嵌套,多个条件时,逐个条件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if语句。

str1=$1
str2=$2
if [ "$str1" = "abc" ];then
    echo "str1 is abc";
    if [ "$str2" = "def" ];then
        echo "str2 is def";
    fi
else
    echo "str1 is not abc";
fi

1.2条件判断case语句

case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac

case 变量引用 in
PAT1)
    分支1
    ;;
PAT2)
    分支2
     ;;
...
*)
    默认分支
    ;;
esac

#支持的通配符

*     #任意长度任意字符
?     #任意单个字符
[]    #指定范围内的任意单个字符
|     #或者,a|b
#判断压缩文件后缀
[root@ubuntu-140 924]#cat back.sh
#!/bin/bash

read -p "please input file:" FILE

FILE=`echo $FILE | grep -Eo "[^.]+$"`

case $FILE in
gz)
 echo gzip
 ;;
bz2)
 echo bzip2
 ;;
xz)
 echo xz
 ;;
Z)
 echo compress
 ;;
zip)
 echo zip
 ;;
*)
 echo other 
 ;;
esac
cmd1 && cmd2 || cmd3

if [ expr1 ];then 
  cmd1 
elif [ expr2 ];then
  cmd2
else
  cmd3
fi

case $var in
part1)
  cmd1
  ;;
part2)
  cmd2
  ;;
*)
  cmd
  ;;
esac

2.循环

2.1.for

格式1

原理:依次将列表中的元素赋值给“变量名”; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束。如果省略 [in WORDS ... ] ,此时使用位置参数变量 in "$@"。

for 循环列表生成方式:
        直接给出列表
        整数列表:如 {start..end}
        返回列表的命令:如 $(COMMAND)
        使用glob,如:*.sh
        变量引用,如:$@,$*,$#

for NAME [in WORDS ... ] ; do COMMANDS; done

#方式1
for 变量名 in 列表;do
    循环体
done

#方式2
for 变量名 in 列表
do
    循环体
done
#计算1...100结果
[root@ubuntu2204 ~]# sum=0;for i in {1..100};do let sum+=i;done ;echo $sum
sum=5050
[root@ubuntu2204 ~]# seq -s+ 100|bc
5050
[root@ubuntu2204 ~]# echo {1..100}|tr ' ' +|bc
5050
[root@ubuntu2204 ~]# seq 100|paste -sd +|bc
5050

#100以内的奇数之和
[root@ubuntu2204 ~]# sum=0;for i in {1..100..2};do let sum+=i;done;echo sum=$sum
sum=2500
[root@ubuntu2204 ~]# seq -s+ 1 2 100| bc
2500
[root@ubuntu2204 ~]# echo {1..100..2}|tr ' ' + | bc
2500

#从变量中获取列表
[root@ubuntu-140 924]#cat a1.sh
#!/bin/bash

sum=0
for i in $*;do
  let sum+=i 

done
echo sum=$sum 
[root@ubuntu-140 924]#./a1.sh 1 2 3 4 5
sum=15                                       

格式2

双小括号方法,即((…))格式,也可以用于算术运算,双小括号方法也可以使bash Shell实现C语言风格的。

for (( exp1; exp2; exp3 )); do COMMANDS; done

for ((控制变量初始化;条件判断表达式;控制变量的修正表达式))
do
循环体
done

说明:控制变量初始化:仅在运行到循环代码段时执行一次;控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断。

[root@ubuntu2204 ~]# cat sum.sh
#!/bin/bash

for((sum=0,i=1;i<=100;i++));do                       
       let sum+=i
done
echo sum=$sum

for((sum=0,i=1;i<=100;sum+=i,i++));do
 true
done
echo sum=$sum

[root@ubuntu1804 ~]#bash sum.sh
sum=5050
sum=5050

2.2.while

while CONDITION; do COMMANDS; done

while CONDITION; do
    循环体
done

while CONDITION
do
    循环体
done

说明:CONDITION:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为 “true”,则执行一次循环;直到条件测试状态为“false”终止循环,因此:CONDTION一般应该有循环控制变量;而此变量的值会在循环体不断地被修正。

无限循环

while true; do
    循环体
done

while : ; do
    循环体
done
[root@ubuntu2204 ~]# sum=0;i=1;while ((i<=100));do let sum+=i;let i++;done;echo $sum
5050

特殊用法:while 循环的特殊用法,遍历文件或文本的每一行

while read line; do
    循环体
done < /PATH/FROM/SOMEFILE

说明:依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line。

2.3.until

until CONDITION; do COMMANDS; done

until CONDITION; do
    循环体
done

无限循环

until false; do
    循环体
done

说明:进入条件: CONDITION 为false;退出条件: CONDITION 为true。

[root@ubuntu2204 ~]# sum=0;i=1;until ((i>100));do let sum+=i;let i++;done;echo $sum
5050
[root@ubuntu2204 ~]# sum=0;i=1;until ! ((i<=100));do let sum+=i;let i++;done;echo $sum
5050

2.4.循环控制语句 continue

continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层。

while CONDITION1; do
   CMD1
   ...
if CONDITION2; then
   continue
fi
   CMD
   ...
done
[root@ubuntu2204 ~]# for i in {1..5};do [ $i -eq 3 ] && continue;echo $i;done
1
2
4
5
[root@ubuntu2204 ~]# for i in {1..5};do if [ $i -eq 3 ];then continue;fi; echo $i;done
1
2
4
5

#循环嵌套中的continue
[root@ubuntu2204 ~]# cat continue_for.sh
#!/bin/bash
for i in {1..4}; do
  for j in {a..c};do
    if [ $j = 'b' ]; then
      continue
    fi
    echo $i $j
  done
  echo
done
#执行
[root@ubuntu2204 ~]# bash continue_for.sh
1 a
1 c

2 a
2 c

3 a
3 c

4 a
4 c
[root@ubuntu2204 ~]# cat continue_for.sh
#!/bin/bash
for i in {1..4}; do
  for j in {a..c};do
    if [ $j = 'b' ]; then
      continue 2
    fi
    echo $i $j
  done
  echo
done
#执行
[root@ubuntu2204 ~]# bash continue_for.sh
1 a
2 a
3 a
4 a

2.5循环控制语句 break

break [N]:提前结束第N层整个循环,最内层为第1层

while CONDITION1; do
   CMD1
   ...
if CONDITION2; then
   break
fi
   CMD
   ...
done
[root@ubuntu2204 ~]# for i in {1..5};do [ $i -eq 3 ] && break;echo $i;done
1
2

#嵌套循环中的break
[root@ubuntu2204 ~]# cat break_for.sh
#!/bin/bash
for i in {1..4}; do
   for j in {a..c};do
     if [ $j = 'b' ]; then
     break
     fi
     echo $i $j
   done
   echo
done
#执行
[root@ubuntu2204 ~]# bash break_for.sh 
1 a

2 a

3 a

4 a
[root@ubuntu2204 ~]# cat break_for.sh
#!/bin/bash
for i in {1..4}; do
   for j in {a..c};do
      if [ $j = 'b' ]; then
      break 2
      fi
      echo $i $j
   done
   echo
done
#执行
[root@ubuntu2204 ~]# bash break_for.sh
1 a

2.5循环控制 shift 命令

shift [n] 用于将参量列表 list 左移指定次数,缺省为左移一次。参量列表 list 一旦被移动,最左端的那个参数就从列表中删除。while 循环遍历位置参量列表时,常用到 shift。

[root@ubuntu2204 ~]# cat shift.sh
#!/bin/bash
until [ -z "$1" ]; do
  echo "$@ "
  shift
done
[root@ubuntu2204 ~]# bash s.sh a b c d e f g
a b c d e f g 
b c d e f g 
c d e f g 
d e f
e f g
f g 
g

[root@ubuntu2204 ~]# cat shift.sh 
#!/bin/bash
while [ -n "$1" ];do
   echo $1
   shift
done
[root@ubuntu2204 ~]# bash shift.sh a b c d
a
b
c
d

[root@ubuntu2204 ~]# cat shift2.sh 
#!/bin/bash
while [ -n "$1" ];do
   echo $1
   shift 2
   if [ $? -ne 0 ];then
      break
    fi
done
[root@ubuntu2204 ~]# bash shift2.sh 1 2 3 4 5 6
1
3
5

2.6循环与菜单 select

select NAME [in WORDS ... ;] do COMMANDS; done

select NAME in list ;do 
  循环体命令
done

说明:

select 循环主要用于创建菜单,按数字顺序排列的菜单项显示在标准错误上,并显示 PS3 提示符, 等待用户输入。
用户输入菜单列表中的某个数字,执行相应的命令。用户输入菜单列表中的某个数字,会将对应的WORD值赋值给NAME变量。用户输入被保存在内置变量 REPLY 中。
select 是个无限循环,因此要用 break 命令退出循环,或用 exit 命令终止脚本。也可以按 ctrl+c 退出循环。
select 经常和 case 联合使用。
与 for 循环类似,可以省略 in list,此时使用位置参量。

[root@ubuntu2204 ~]# cat select.sh 
#!/bin/bash

PS3="请选择: "
select i in 备份数据库 清理日志 重启服务 退出; do
case $REPLY in
1)
 echo "开始备份数据库..."
 sleep 2
 echo "数据库备份完成"
 ;;
2)
 echo "开始清理日志..."
 sleep 2
 echo "清理完成"
 ;;
3) 
 echo "开始重启服务..."
 sleep 2
 echo "重启完成"
 ;;
4)
 break
 ;;
*)
 echo "选项错误"
 ;; 
esac
done

3.函数

函数由两部分组成:函数名和函数,Shell 函数是由多条命令组成的代码块,用于代码重用和模块化编程,它与 Shell 程序的关键区别在于:函数在当前 Shell 环境中运行(可修改当前 Shell 的变量),而 Shell 程序在子 Shell 中运行(变量修改不影响父 Shell)。

帮助:help function

3.1定义函数

#语法一
func_name(){
  ...函数体...
}

#语法二
function func_name {
  ...函数体...
}

#语法三
function func_name(){
  ...函数体...
}

3.2查看函数

#查看当前已定义的函数名
declare -F

#查看当前已定义的函数定义
declare -f

#查看指定当前已定义的函数名
declare -f func_name 

#查看当前已定义的函数名定义
declare -F func_name

3.3删除函数

unset func_name

3.4函数调用

函数的调用方式:可在交互式环境下定义函数、可将函数放在脚本文件中作为它的一部分、可放在只包含函数的单独文件中。
函数只有被调用才会执行,通过给定函数名调用函数,函数名出现的地方,会被自动替换为函数代码。
函数的生命周期:被调用时创建,返回时终止。

交互式环境调用函数

定义
[root@ubuntu2204 ~]# test_func(){
> echo "this is cli function"
> }
#调用
[root@ubuntu2204 ~]# test_func
this is cli function
#查看
[root@ubuntu2204 ~]# declare -f test_func
test_func () 
{ 
    echo "this is cli function"
}

在脚本中定义及使用函数

函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell首次发现它后才能使用,调用函数仅使用其函数名即可。

[root@ubuntu2204 ~]# cat func1.sh 
#!/bin/bash

#函数定义
hello(){
  echo "this is test function - hello"
}
#第一次调用
hello
#第二次调用
hello

#函数调用函数
[root@ubuntu2204 ~]# cat func2.sh 
#!/bin/bash
func1(){
  echo "func1"
  func2
}
func2(){
  echo "func2"
}
func1
#调用
[root@ubuntu2204 ~]# bash func2.sh
func1
func2
#函数间互相调用,死循环,爆栈,耗尽资源,高级语言会有此错误检测
[root@ubuntu2204 ~]# cat func2.sh 
#!/bin/bash
func1(){
  echo "func1"
  sleep 1
  func2
}
func2(){
  echo "func2"
  sleep 1
  func1
}
func1

3.5使用函数文件

可以将经常使用的函数存入一个单独的函数文件,然后将函数文件载入shell,再进行调用函数。函数文件名可任意选取,但最好与相关任务有某种联系,例如:functions 。一旦函数文件载入shell,就可以在命令行或脚本中调用函数。可以使用delcare -f 或set 命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数。若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入此文件。

实现函数文件的过程:
1. 创建函数文件,只存放函数的定义
2. 在shell脚本或交互式shell中加载函数文件
3. 调用函数

#定义函数文件
[root@ubuntu2204 ~]# cat functions
#!/bin/bash
func1(){
 echo "functions-func1"
}
func2(){
 echo "functions-func2"
}

#命令行引入
[root@ubuntu2204 ~]# . functions
#这种写法也可以
[root@ubuntu2204 ~]# source functions

#函数调用
[root@ubuntu2204 ~]# func1;func2
functions-func1
functions-func2

#脚本文件中引用
[root@ubuntu2204 ~]# cat func3.sh 
#!/bin/bash
. functions
func1
func2

#执行
[root@ubuntu2204 ~]# bash func3.sh
functions-func1
functions-func2

3.6函数返回值

函数默认返回值是函数体中最后一条命令的退出状态码;也可以使用return 自定义函数的返回值,在函数中使用 return,return 之后的语句将不会被执行。

return语句

返回值

return由return 语句的前一行命令执行结果决定
return 0无错误返回
return 1-255有错误返回
[root@ubuntu2204 ~]# cat var4.sh
#!/bin/bash
var_func1(){
 echo "func1 ======="
}
#默认返回值
var_func2(){
 echo "func2 ======="
 return
}
var_func3(){
 echo "func3 ======"
 errcmd
}
#自定义返回值,return 之后的语句将不会被执行
var_func4(){
 echo "func4 ======"
 return 123
 echo "func4 ++++++="
}
var_func1
echo -e "func1 返回值是 $?\n"
var_func2
echo -e "func2 返回值是 $?\n"
var_func3
echo -e "func3 返回值是 $?\n"
var_func4
echo -e "func4 返回值是 $?\n"

#执行
[root@ubuntu2204 ~]# bash var4.sh
func1 =======
func1 返回值是 0

func2 =======
func2 返回值是 0

func3 ======
var4.sh: line 26: errcmd: command not found
func3 返回值是 127

func4 ======
func4 返回值是 123

return和exit区别

return 只会中断函数执行,但exit 会中断整个脚本的执行。

root@ubuntu2204 ~]# cat var5.sh 
#!/bin/bash
var_func1(){
  echo "func1 ======="
  return 123
  echo "func1 ======="
}

echo 123
var_func1
echo "func1 返回值是 "$?
echo 456

[root@ubuntu2204 ~]# bash var5.sh
123
func1 =======
func1 返回值是 123
456

[root@ubuntu2204 ~]# cat var6.sh
#!/bin/bash
var_func1(){
  echo "func1 ======="
  exit 123
  echo "func1 ======="
}

echo 123
var_func1
echo "func1 返回值是 "$?
echo 456

[root@ubuntu2204 ~]# bash var6.sh
123
func1 =======

[root@ubuntu2204 ~]# echo $?
123

3.7环境函数

类拟于环境变量,也可以定义环境函数,使子进程也可使用父进程定义的函数。

#定义环境函数
export -f function_name 
declare -xf function_name

查看环境函数
export -f
declare -xf
[root@ubuntu2204 ~]# cli_func1(){ echo "cli-func1"; }; cli_func2(){ echo "cli-func2"; };export -f cli_func2;

[root@ubuntu2204 ~]# export -f
cli_func2 () 
{ 
    echo "cli-func2"
}
declare -fx cli_func2

#调用
[root@ubuntu2204 ~]# cat var7.sh 
#!/bin/bash
cli_func1
cli_func2

#执行
[root@ubuntu2204 ~]# ./var7.sh 
./var7.sh: line 16: cli_func1: command not found
cli-func2

3.8函数参数

传递参数给函数:在函数名后面以空白分隔给定参数列表即可,如:testfunc arg1 arg2 ... 
在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用$@,$*, $#等特殊变量

[root@ubuntu-140 924]#cat test.sh
!/bin/bash

func1(){

  echo $1
  echo $2
  echo $3
}

echo $1
echo $2
echo $3
echo
func1 $3 $2 $1
[root@ubuntu-140 924]#./test.sh x y z
./test.sh: line 1: !/bin/bash: No such file or directory
x
y
z

z
y
x

3.9函数变量

变量类型特点
普通变量只在当前shell进程中有效,函数外定义,可以在函数内修改
环境变量当前shell和子shell有效
本地变量作用域在函数内,函数结束会被自动销毁

函数中定义本地变量

local NAME=VALUE
#普通变量可以在函数中被修改
[root@ubuntu2204 ~]# cat functions 
#!/bin/bash
var_func2(){
  echo "func2 start ==================="
  echo $var1
  var1=789
  echo $var1
  echo "func2 end ====================="
}

[root@ubuntu2204 ~]# cat var1.sh
#!/bin/bash
. functions
var1=123
var_func1(){
  echo "func1 start ==================="
  echo $var1
  var1=456
  echo $var1
  echo "func1 end ===================="
}

echo $var1
var_func1
echo $var1
var_func2
echo $var1

#执行
[root@ubuntu2204 ~]# bash var1.sh
123
func1 start ===================
123
456
func1 end ====================
456
func2 start ===================
456
789
func2 end =====================
789

#本地变量只能在函数内定义
[root@ubuntu2204 ~]# cat var2.sh 
#!/bin/bash
local var1=123
echo $var1

#执行报错
[root@ubuntu2204 ~]# bash var2.sh
var2.sh: line 14: local: can only be used in a function

#本地变量只作用在函数内
[root@ubuntu2204 ~]# cat var2.sh 
#!/bin/bash
var1=123
var_func1(){
  echo "func1 start ========================"
  echo $var1
  local var1=456
  local var2=789
  echo $var1 $var2
  echo "func1 end ========================="
}
echo $var1 $var2
var_func1
echo $var1 $var2

#执行
[root@ubuntu2204 ~]# bash var2.sh
123
func1 start ========================
123
456 789
func1 end =========================
123

#环境变量可以传到子进程中
[root@ubuntu2204 ~]# cat var33.sh
#!/bin/bash
echo "var1=$var1 var2=$var2 var3=$var3"

[root@ubuntu2204 ~]# cat var3.sh 
#!/bin/bash
var1=123
export var2=456
declare -x var3=789
bash var33.sh
echo "var1=$var1 var2=$var2 var3=$var3"

[root@ubuntu2204 ~]# bash var3.sh 
var1= var2=456 var3=789

4.递归

函数递归是函数直接或间接调用自身的编程技术,需确保有明确的终止条件(出口语句),否则会导致无限递归甚至栈溢出。

#无出口递归
[root@ubuntu2204 ~]# num=1;cli_func1(){ echo "num="$num; let num++; cli_func1; };cli_func1
......
......
num=8587
Connection closed.
Disconnected from remote host(rocky86-150) at 11:44:30.
Type `help' to learn how to use Xshell prompt.
#阶乘
n!=1×2×3×...×(n-1)×n
0!=1, n!=(n-1)!×n

[root@ubuntu2204 ~]# cat fac.sh 
#!/bin/bash
fac(){
   if [ $1 -gt 1 ];then
       echo $[$1 * $(fac $[$1-1])]
   else
       echo 1
   fi
}

fac $1

[root@ubuntu2204 ~]# bash fac.sh 5
120
#斐波拉契数列
F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)

[root@ubuntu2204 ~]# cat fib.sh 
#!/bin/bash
fib(){
   if [ $1 -gt 1 ];then
   echo  $[ $(fib $[$1-1]) + $(fib $[$1-2]) ] 
   else
   echo $1
   fi
}

fib $1

十一、其他脚本相关工具

1.信号捕捉trap

trap 命令可以捕捉信号,修改信号原来的功能,实现自定义功能。在脚本或程序的执行过程中,我们可以通过发送信号的方式,打断或终止程序的执行过程,为了避免这种情况,我们可以使用信号捕捉,来自定义信号处理。

trap [-lp] [[arg] signal_spec ...]

#常用选项
-l                     #显示所有信号
-p                     #显示所有自定义的信号
trap 'command' signal  #自定义指定信号的处理方式
trap '' signal         #忽略指定的信号
trap '-' signal        #恢复信号默认操作
trap func EXIT         #退出时执行func

信号的三种表示方法

3) SIGQUIT

3       #信号ID

SIGQUIT #完整写法,大小写都支持
QUIT    #简短写法,大小写都支持
[root@ubuntu2204 ~]# cat trap.sh
#!/bin/bash
trap "echo '捕捉到了 ctrl+c信号'" SIGINT
trap "echo '捕捉到了 ctrl+\信号'" quit
trap -p
for i in {1..15};do
   sleep 1
   echo $i
done

trap '' int
trap -p
for i in {16..30};do
   sleep 1
   echo $i
done

trap '-' int
trap -p
for i in {31..45};do
   sleep 1
   echo $i
done

#
[root@ubuntu2204 ~]# cat trap_fin.sh
#!/bin/bash
finish(){
   echo "finish at `date +%F-%T`" | tee -a /tmp/trap_fin.log
}

trap finish exit

while true;do
   echo "`date +%F-%T`"
   sleep 1
done

2.创建临时文件mktemp

mktemp 命令用于创建并显示临时文件,可避免冲突,默认放在/tmp目录下。

mktemp [OPTION]... [TEMPLATE]

#常用选项
-d|--directory          #创建目录
-u|--dry-run            #只输出命令,不执行
-p DIR|--tmpdir[=DIR]   #指明临时文件所存放目录位置
-t                      #将文件保存到$TMPDIR 定义的目录中,如果该变量未定义,则保存到/tmp 目录中
[root@ubuntu2204 ~]# mktemp
/tmp/tmp.YQXxc3bHdI
[root@ubuntu2204 ~]# mktemp XXXX
UYtW
#如果指定了文件名,则后面一定要有X,至少要3个,X会被替换成随机串
[root@ubuntu2204 ~]# mktemp test
mktemp: too few X's in template ‘test’
[root@ubuntu2204 ~]# mktemp testXXX
test19C
#创建并赋值给变量
[root@ubuntu2204 ~]# tmp=`mktemp`
[root@ubuntu2204 ~]# echo $tmp
/tmp/tmp.wQAdrqn1UR
#创建目录
[root@ubuntu2204 ~]# mktemp -d
/tmp/tmp.2iLac1ruHt
[root@ubuntu2204 ~]# ll /tmp/tmp.2iLac1ruHt/
total 0

3.安装复制文件install

install 功能相当于cp,chmod,chown,chgrp ,mkdir 等相关工具的集合。

install [OPTION]... [-T] SOURCE DEST
install [OPTION]... SOURCE... DIRECTORY
install [OPTION]... -t DIRECTORY SOURCE...
install [OPTION]... -d DIRECTORY...

#常用选项
-m|--mode=MODE       #指定权限,默认755
-v|--verbose         #显示过程
-o|--owner=OWNER     #指定属主
-g|--group=GROUP     #指定属组
-d|--directory DIR   #指定目录,如果不存在就创建
#如果目录不存在,则创建目录,并将权限设成777,如果目录存在,则修改其权限
[root@ubuntu2204 ~]# install -m 777 -d testdir
[root@ubuntu2204 ~]# ll -d testdir
drwxrwxrwx 2 root root 6 May 25 12:46 testdir

#将文件复制到指定目录,并指定属主属组,权限
[root@ubuntu2204 ~]# install -m 777 -o mage -g root fin.sh /tmp/fin-tmp.sh
[root@ubuntu2204 ~]# ll /tmp/fin-tmp.sh
-rwxrwxrwx 1 mage root 153 May 25 12:58 /tmp/fin-tmp.sh

十二、数组

1.声明数组

#普通数组可以不事先声明,直接使用
declare -a ARRAY_NAME

#关联数组必须先声明,再使用
declare -A ARRAY_NAME

#注意:两者不可相互转换

2. 数组赋值

一次只赋值一个元素

ARRAY_NAME[INDEX]=VALUE
[root@ubuntu2204 ~]# weekdays[0]="Sunday"
[root@ubuntu2204 ~]# weekdays[4]="Thursday"

一次赋值全部元素

ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)
[root@ubuntu2204 ~]# title=("ceo" "coo" "cto")
[root@ubuntu2204 ~]# num=({0..10})
[root@ubuntu2204 ~]# alpha=({a..g})
[root@ubuntu2204 ~]# file=( *.sh )

只赋值特定元素

ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)
[root@ubuntu2204 ~]# weekdays=([0]="Sunday",[4]="Thursday")

交互式数组值对赋值

read -a ARRAY
[root@ubuntu2204 ~]# read -a test
a b c d

3. 显示所有数组

declare -a

[root@ubuntu-140 924]#declare -a
declare -a BASH_ARGC=([0]="0")
declare -a BASH_ARGV=()
declare -a BASH_COMPLETION_VERSINFO=([0]="2" [1]="11")
declare -a BASH_LINENO=()
declare -a BASH_SOURCE=()
declare -ar BASH_VERSINFO=([0]="5" [1]="1" [2]="16" [3]="1" [4]="release" [5]="x86_64-pc-linux-gnu")
declare -a DIRSTACK=()
declare -a FUNCNAME
declare -a GROUPS=()
declare -a PIPESTATUS=([0]="0")

4. 引用数组

4.1 引用特定的数组元素

#如果省略[INDEX]表示引用下标为0的元素
${ARRAY_NAME[INDEX]}

[root@ubuntu2204 ~]# declare -a title=([0]="ceo" [1]="coo" [2]="cto")
[root@ubuntu2204 ~]# echo ${title[1]}
coo
[root@ubuntu2204 ~]# echo ${title}
ceo
[root@ubuntu2204 ~]# echo ${title[2]}
cto
[root@ubuntu2204 ~]## echo ${title[3]}

[root@rocky86 ~]#
#区分
[root@ubuntu2204 ~]# echo $title
ceo
[root@ubuntu2204 ~]# echo ${title[0]}
ceo
[root@ubuntu2204 ~]# echo $title[0]
ceo[0]

说明:在 Bash 中引用数组时,如果未指定下标(如 ${array} 或 $array),默认只返回数组的第一个元素(即下标为 0 的值),而非整个数组。

4.2引用数组所有元素

${ARRAY_NAME[*]}
${ARRAY_NAME[@]}
[root@ubuntu2204 ~]# echo ${title[@]}
ceo coo cto
[root@ubuntu2204 ~]# echo ${title[*]}
ceo coo cto

遍历数组

[root@ubuntu2204 ~]# arr=({a..e})
[root@ubuntu2204 ~]# for i in ${arr[*]};do echo $i; done;
a
b
c
d
e
[root@ubuntu2204 ~]# unset arr[2];unset arr[4]
#用连续下标遍历,下标不连续时会有问题
[root@ubuntu2204 ~]# for ((i=0;i<${#arr[*]};i++));do echo ${arr[$i]};done
a
b
        #数组2下标删除所以无输出
[root@ubuntu2204 ~]#
#先取出所有下标,再遍历
[root@ubuntu2204 ~]# for i in ${!arr[@]};do echo ${arr[$i]};done
a
b
d
e

4.3数组的长度

元素中的个数

${#ARRAY_NAME[*]}
${#ARRAY_NAME[@]}
[root@ubuntu2204 ~]# echo ${#title[*]}
3

4.4数组的所有下标

${!ARRAY_NAME[*]}
${!ARRAY_NAME[@]}
[root@ubuntu2204 ~]# echo ${!title[@]}
0 1 2
[root@ubuntu2204 ~]# echo ${!title[*]}
0 1 2

5.删除数组

5.1删除数组中单个元素

删除数组中的某元素,会导致稀疏格式

unset ARRAY[INDEX]
[root@ubuntu2204 ~]# echo ${title[*]}
ceo coo cto
[root@ubuntu2204 ~]# unset title[1]
[root@ubuntu2204 ~]# echo ${title[*]}
ceo cto

5.2删除整个数组

unset ARRAY

6.数组数据处理

6.1数组切片

${ARRAY[@]:offset:number}
${ARRAY[*]:offset:number}

offset #要跳过的元素个数
number #要取出的元素个数

#取偏移量之后的所有元素
{ARRAY[@]:offset}
{ARRAY[*]:offset}
[root@ubuntu2204 ~]# num=({0..10})
[root@ubuntu2204 ~]# echo ${num[*]}
0 1 2 3 4 5 6 7 8 9 10
[root@ubuntu2204 ~]# echo ${num[*]:2:3}
2 3 4
[root@ubuntu2204 ~]# echo ${num[*]:6}
6 7 8 9 10

6.2向数组中追加元素

ARRAY[${#ARRAY[*]}]=value
ARRAY[${#ARRAY[@]}]=value
[root@ubuntu2204 ~]# num[${#num[@]}]=11
[root@ubuntu2204 ~]# echo ${#num[@]}
12
[root@ubuntu2204 ~]# echo ${num[@]}
0 1 2 3 4 5 6 7 8 9 10 11

7.关联数组

declare -A ARRAY_NAME 
ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2‘...)

[root@ubuntu2204 ~]# test[a]=123
[root@ubuntu2204 ~]# echo ${test[*]}
123
[root@ubuntu2204 ~]# test[b]=456
[root@ubuntu2204 ~]# echo ${test[*]}
456

#此处再声明就失败,因为test现在是普通数组
[root@ubuntu2204 ~]# declare -A test
-bash: declare: test: cannot convert indexed to associative array

[root@ubuntu2204 ~]# unset test
[root@ubuntu2204 ~]# declare -A test
[root@ubuntu2204 ~]# test[a]=111
[root@ubuntu2204 ~]# test[b]=222
[root@ubuntu2204 ~]# echo ${test[a]}
111
[root@ubuntu2204 ~]## echo ${test[c]}

[root@ubuntu2204 ~]# echo ${test[b]}
222
[root@ubuntu2204 ~]# echo ${test[*]}
111 222

#遍历
[root@ubuntu2204 ~]# for i in ${test[*]};do echo ${i};done
111
222

#遍历下标,根据下标取元素内容
[root@ubuntu2204 ~]# for i in ${!test[*]};do echo ${test[$i]};done
111
222
————————————————————————————————————————————————————————————————————————————————————————
#关联数组赋值必须指定下标
#报错
[root@ubuntu2204 ~]# declare -A studentList
[root@ubuntu2204 ~]# studentList=(tom jerry)
-bash: studentList: tom: must use subscript when assigning associative array
-bash: studentList: jerry: must use subscript when assigning associative array
[root@ubuntu2204 ~]# studentList=([stu1]=tom [stu2]=jerry)
[root@ubuntu2204 ~]# echo ${studentList[*]}
jerry tom
[root@ubuntu2204 ~]# echo ${!studentList[*]}
stu2 stu1
#根据下标遍历
[root@ubuntu2204 ~]# for i in ${!studentList[*]};do echo $i=${studentList[$i]};done
stu2=jerry
stu1=tom
————————————————————————————————————————————————————————————————————————————————————————
#显示所有关联数组下标
[root@ubuntu2204 ~]# declare -A title=([a]=111e [b]=222 [c]=333)
[root@ubuntu2204 ~]# echo ${!title[@]}
a b c

[root@ubuntu2204 ~]# declare -A student

[root@ubuntu2204 ~]# student[name1]=aaa
[root@ubuntu2204 ~]# student[name2]=bbb

[root@ubuntu2204 ~]# student[age1]=18
[root@ubuntu2204 ~]# student[age2]=16

[root@ubuntu2204 ~]# echo ${student[*]}
16 18 bbb aaa

[root@ubuntu2204 ~]# echo ${!student[*]}
age2 age1 name2 name1

[root@ubuntu2204 ~]# for i in {1..5};do echo student{name$i]=${student[name$i]};done
student{name1]=aaa
student{name2]=bbb
student{name3]=
student{name4]=
student{name5]=

[root@ubuntu2204 ~]# for i in {1..2};do echo ${student[name$i]}: ${student[age$i]};done;
aaa: 18
bbb: 16

十三、字符串处理

1.字符串切片

1.1基于偏移量取字符串

${#var} #返回字符串变量var的字符的长度,一个汉字算一个字符

${var:offset} #返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,到最后的部分,offset的取值在0 到 ${#var}-1 之间(bash4.2后,允许为负值)

${var:offset:number} #返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,长度为number的部分

${var: -length} #取字符串的最右侧几个字符, 注意:冒号后必须有一空白字符

${var:offset:-length} #从最左侧跳过offset字符,一直向右取到距离最右侧lengh个字符之前的内容,即:掐头去尾

${var: -length:-offset} #先从最右侧向左取到length个字符开始,再向右取到距离最右侧offset个字符之间的内容,注意:-length前空格,并且length必须大于offset
[root@ubuntu2204 ~]# str=abcd1234
[root@ubuntu2204 ~]# echo ${#str}
8
[root@ubuntu2204 ~]# echo ${str:2}
cd1234
[root@ubuntu2204 ~]# echo ${str:2:4}
cd12
[root@ubuntu2204 ~]# echo ${str: -3}
234
[root@ubuntu2204 ~]# echo ${str:2:-1}
cd123
[root@ubuntu2204 ~]#echo ${str: -6:-2}
cd12

#一个中文算一个字符长度
[root@ubuntu2204 ~]# str2=我爱中国
[root@ubuntu2204 ~]# echo ${#str2}
4

1.2基于模式取子串

#其中word可以是指定的任意字符,自左而右,查找var变量所存储的字符串中,第一次出现的word, 删除字符串开头至第一次出现word字符串(含)之间的所有字符,即懒惰模式,以第一个word为界删左留右
${var#*word}

#从var变量的值中删除以word开头的部分
${var#word}

#同上,贪婪模式,不同的是,删除的是字符串开头至最后一次由word指定的字符之间的所有内容,即贪婪模式,以最后一个word为界删左留右
${var##*word}
${var##word}
[root@ubuntu2204 ~]# str=abcd1234abcd1234

#删除第一次出现的cd及其左边的内容
[root@ubuntu2204 ~]# echo ${str#*cd}
1234abcd1234

#删除左边第一个ab
[root@ubuntu2204 ~]# echo ${str#ab}
cd1234abcd1234

#删除最后第一次出现的cd及其左边的内容
[root@ubuntu2204 ~]# echo ${str##*cd}
1234
[root@ubuntu2204 ~]# echo ${str##ab}
cd1234abcd1234
#其中word可以是指定的任意字符,功能:自右而左,查找var变量所存储的字符串中,第一次出现的word, 删除字符串最后一个字符向左至第一次出现word字符串(含)之间的所有字符,即懒惰模式,以从右向左的第一个word为界删右留左
${var%word*}
${var%word}

#同上,只不过删除字符串最右侧的字符向左至最后一次出现word字符之间的所有字符,即贪婪模式,以从右向左的最后一个word为界删右留左
${var%%word*}
${var%%word}
[root@ubuntu2204 ~]# str=abcd1234abcd12345

#从右边开始查找,删除第一个ab及右边的字符串
[root@ubuntu2204 ~]# echo ${str%ab*}
abcd1234

#删除右边的12345
[root@ubuntu2204 ~]# echo ${str%12345}
abcd1234abcd

#从右边开始查找,删除最后一个123及右边的字符串
[root@ubuntu2204 ~]# echo ${str%%123*}
abcd

#删除右边第一12345
[root@ubuntu2204 ~]# echo ${str%%12345}
abcd1234abcd

1.3查找替换

#查找var所表示的字符串中,第一次被pattern所匹配到的字符串,以substr替换之,懒惰模式
${var/pattern/substr}

#查找var所表示的字符串中,所有能被pattern所匹配到的字符串,以substr替换之,贪婪模式
${var//pattern/substr}

#查找var所表示的字符串中,行首被pattern所匹配到的字符串,以substr替换之
${var/#pattern/substr}

#查找var所表示的字符串中,行尾被pattern所匹配到的字符串,以substr替换之
${var/%pattern/substr}
[root@ubuntu2204 ~]# str=abcd1234abcd12345

#从左到右,替换第一个123为XYZ
[root@ubuntu2204 ~]# echo ${str/123/XYZ}
abcdXYZ4abcd12345
#从左到右,替换所有123为XYZ
[root@ubuntu2204 ~]# echo ${str//123/XYZ}
abcdXYZ4abcdXYZ45
#替换行首的abc为XYZ
[root@ubuntu2204 ~]# echo ${str/#abc/XYZ}
XYZd1234abcd12345
#替换行尾的12345为XYZ
[root@ubuntu2204 ~]# echo ${str/%12345/XYZ}
abcd1234abcdXYZ
#删除var表示的字符串

1.4查找并删除

#删除var表示的字符串中第一次被pattern匹配到的字符串,懒惰模式
${var/pattern}

#删除var表示的字符串中所有被pattern匹配到的字符串,贪婪模式
${var//pattern}

#删除var表示的字符串中所有以pattern为行首匹配到的字符串
${var/#pattern}

#删除var所表示的字符串中所有以pattern为行尾所匹配到的字符串
${var/%pattern}
[root@ubuntu2204 ~]# str=abcd1234abcd12345

#从左到右删除第一个1234
[root@ubuntu2204 ~]# echo ${str/1234}
abcdabcd12345
#从左到右删除所有1234
[root@ubuntu2204 ~]# echo ${str//1234}
abcdabcd5
#删除行首abcd
[root@ubuntu2204 ~]# echo ${str/#abcd}
1234abcd12345
#删除行尾12345
[root@ubuntu2204 ~]# echo ${str/%12345}
abcd1234abcd

1.5字符大小写转换

#把var中的所有小写字母转换为大写
${var^^}

#把var中的所有大写字母转换为小写
${var,,}
[root@ubuntu2204 ~]# str=abcd1234ABCD12345

#所有小写转大写
[root@ubuntu2204 ~]# echo ${str^^}
ABCD1234ABCD12345
#所有大写转小写
[root@ubuntu2204 ~]# echo ${str,,}
abcd1234abcd12345
#tr实现大小写转换
[root@ubuntu2204 ~]# echo $str | tr 'a-z' 'A-Z'
ABCD1234ABCD12345

1.6变量扩展

#扩展以所有prefix开头的变量

${!prefix*}
${!prefix@}
[root@ubuntu2204 ~]# file1=a;file2=b;file3=c
[root@ubuntu2204 ~]# echo ${!file*}
file1 file2 file3
[root@ubuntu2204 ~]# echo ${!file@}
file1 file2 file3

十四、高级变量

1.高级变量赋值

$str 为变量名,expr 为具体字符串,这些组合可以省掉一些 if,else 的判断代码。

[root@ubuntu2204 ~]# str1=abc
[root@ubuntu2204 ~]# echo ${str1-xyz}
abc
[root@ubuntu2204 ~]# str1=""
[root@ubuntu2204 ~]# echo ${str1-xyz}
[root@ubuntu2204 ~]#
[root@ubuntu2204 ~]# unset str1
[root@ubuntu2204 ~]# echo ${str1-xyz}
xyz

2.高级变量用法-有类型变量

declare [-aAfFgilnrtux] [-p] [name[=value] ...]

#选项:
-f         #显示已定义的所有函数名及其内容
-F         #仅显示已定义的所有函数名
-p         #显示每个变量的属性和值
-a         #声明或显示定义为数组的变量
-A         #将变量定义为关联数组      
-i         #声明或显示定义为整型的变量
-l         #声明或显示定义为小写的变量
-n         #变量引用另外一个变量的值
-r         #声明或显示只读变量
-t         #声明或显示具有trace(追踪)属性的变量
-u         #声明或显示定义为大写的变量
-x         #显示环境变量和函数,相当于export
#显示所有己定义的函数和函数体
[root@ubuntu2204 ~]# declare -f

#显示所有己定义的函数,仅函数名
[root@ubuntu2204 ~]# declare -F

#显示所有己定义的变量属性及值
[root@ubuntu2204 ~]# declare -p

#显示所有普通数组
[root@ubuntu2204 ~]# declare -a

#定义一个普通数组
[root@ubuntu2204 ~]# declare -a arr1

#显示所有关联数组
[root@ubuntu2204 ~]# declare -A

#定义一个关联数组
[root@ubuntu2204 ~]# declare -A arr2

#显示所有整型变量
[root@ubuntu2204 ~]# declare -i

#定义一个整型变量
[root@ubuntu2204 ~]# declare -i int1
[root@ubuntu2204 ~]# int1=abcd
[root@ubuntu2204 ~]# echo $int1
0

#显示所有小写变量
[root@ubuntu2204 ~]# declare -l

#定义一个小写变量
[root@ubuntu2204 ~]# declare -l lower1
[root@ubuntu2204 ~]# lower1=1234abcdABCD
[root@ubuntu2204 ~]# echo $lower1
1234abcdabcd

#变量引用
[root@ubuntu2204 ~]# str1=1234
[root@ubuntu2204 ~]# declare -n str2=str1
[root@ubuntu2204 ~]# echo $str1 $str2
1234 1234

#str2 会跟着改变
[root@ubuntu2204 ~]# str1=abcd
[root@ubuntu2204 ~]# echo $str1 $str2
abcd abcd

#str1 也会跟着改变
[root@ubuntu2204 ~]# str2=xyz
[root@ubuntu2204 ~]# echo $str1 $str2
xyz xyz

#显示所有只读变量
[root@ubuntu2204 ~]# declare -r

#只读变量在定义时就要赋值
[root@ubuntu2204 ~]# declare -r r1
[root@ubuntu2204 ~]# r1=123
-bash: r1: readonly variable
[root@ubuntu2204 ~]# declare -r r2=123
[root@ubuntu2204 ~]# echo $r2
123

3.变量间接引用

3.1eval

eval命令将会首先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量,该命令对变量进行两次扫描,二次展开。

[root@ubuntu2204 ~]# CMD=whoami
[root@ubuntu2204 ~]# echo $CMD
whoami

[root@ubuntu2204 ~]# eval $CMD
root

[root@ubuntu2204 ~]# n1=6
[root@ubuntu2204 ~]# echo {1..$n1}
{1..6}
#两次展开
[root@ubuntu2204 ~]# eval echo {1..$n1}
1 2 3 4 5 6
[root@ubuntu2204 ~]# for i in `eval echo {1..$n1}`;do echo i=$i;done
i=1
i=2
i=3
i=4
i=5
i=6

[root@ubuntu2204 ~]# a=aa;b=bb
[root@ubuntu2204 ~]# $a$b=cc
bash: aabb=cc: command not found...
[root@ubuntu2204 ~]# eval $a$b=cc
[root@ubuntu2204 ~]# echo $a$b
aabb
[root@ubuntu2204 ~]# echo $aabb
cc

3.2间接变量引用

如果第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值就称为间接变量引用variable1的值是variable2,而variable2又是变量名,variable2的值为value,间接变量引用是指通过variable1获得变量值value的行为。

variable1=variable2
variable2=value
[root@ubuntu2204 ~]# var1=str
[root@ubuntu2204 ~]# str=abcd
[root@ubuntu2204 ~]# echo $var1
str
[root@ubuntu2204 ~]# echo \$$var1
$str
[root@ubuntu2204 ~]# eval echo \$$var1
abcd
#bash Shell提供了两种格式实现间接变量引用

#方法1
#变量赋值
eval tempvar=\$$variable1
#显示值
eval echo \$$variable1
eval echo '$'$variable1
echo $tmpvar

#方法2
#变量赋值
tempvar=${!variable1}
#显示值
echo ${!variable1}
[root@ubuntu2204 ~]# ceo=name
[root@ubuntu2204 ~]# name=hu

[root@ubuntu2204 ~]# eval echo \$$ceo
hu
[root@ubuntu2204 ~]# eval tmp=\$$ceo
[root@ubuntu2204 ~]# echo $tmp
hu
[root@ubuntu2204 ~]# echo ${!ceo}
hu
[root@ubuntu2204 ~]# tmp2=${!ceo}
[root@ubuntu2204 ~]# echo $tmp2
hu
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值