shell脚本编程-进阶部分
接上一部分shell脚本编程基础
使用read命令来接受输入
使用read来把输入值分配给一个或多个shell变量,read从标准输入中读取值,给每个单词分配一个变 量,所有剩余单词都被分配给最后一个变量,如果变量名没有指定,默认标准输入的值赋值给系统内置 变量REPLY
read [options] [name ...]
#常见选项
-p #指定要显示的提示
-s #静默输入,一般用于密码
-n N #指定输入的字符长度N
-d 'CHAR' #输入结束符
-t N #TIMEOUT为N秒
bash shell 的配置文件
bash shell的配置文件很多,可以分成下面类别
按生效范围划分两类
全局配置:针对所有用户皆有效
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc ------------------------ /etc/bash.bashrc #ubuntu
个人配置:只针对特定用户有效
~/.bash_profile
~/.bashrc
shell登录两种方式分类
交互式登录
- 直接通过终端输入账号密码登录
- 使用 su - UserName 切换的用户
配置文件生效和执行顺序:
#放在每个文件最前
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
~/ .bash_ profile
~/ .bashrc
/etc/bashrc #此文件执行两次
#放在每个文件最后
/etc/profile.d/*.sh
/etc/bashrc
/etc/profile
/etc/bashrc #此文件执行两次
~/.bashrc
~/.bash_profile
注意:文件之间的调用关系,写在同一个文件的不同位置,将影响文件的执行顺序
非交互式登录
- su UserName
- 图形界面下打开的终端
- 执行脚本
- 任何其它的bash实例
执行顺序:
/etc/profile.d/*.sh
/etc/bashrc
~/.bashrc
按功能划分分类
内容 | Profile类 | Bashrc类 |
---|---|---|
交互式登录shell配置 | 是 | 否 |
非交互式登录shell配 置 | 是 | 是 |
全局配置文件 | /etc/profile,/etc/profile.d/*.sh | /etc/bashrc |
个人配置文件 | ~/.bash_profile | ~/.bashrc |
功能作用 | 定义环境变量,运行命令或脚本 | 定义命令别名和函数,定义本地 变量 |
编辑配置文件生效
修改profile和bashrc文件后需生效两种方法:
- 重新启动shell进程
- source|. 配置文件
注意:source 会在当前shell中执行脚本,所有一般只用于执行配置文件,或在脚本中调用另一个脚本的 场景
Bash 退出任务
保存在~/.bash_logout文件中(用户),在退出登录shell时运行
功能:
- 创建自动备份
- 清除临时文件
流程控制
条件选择
选择执行 if 语句
if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ 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
条件判断 case 语句
case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac
case 变量引用 in
PAT1)
分支1
;;
PAT2)
分支2
;;
...
*)
默认分支
;;
esac
#case支持glob风格的通配符
* #任意长度任意字符
? #任意单个字符
[] #指定范围内的任意单个字符
| #或者,a|b
循环
循环执行介绍
将某代码段重复运行多次,通常有进入循环的条件和退出循环的条件
重复运行次数
- 循环次数事先已知
- 循环次数事先未知
常见的循环的命令:for, while, until
程序先进行语句判断,如真则执行循环语句,然后再进行语句判断,直至语句判断失败才跳出;
循环 for
for NAME [in WORDS ... ] ; do COMMANDS; done
#方式1
for 变量名 in 列表;do
循环体
done
#方式2
for 变量名 in 列表
do
循环体
done
执行机制:
- 依次将列表中的元素赋值给“变量名”;每次赋值后即执行一次循环体;直到列表中的元素耗尽,循 环结束
- 如果省略 [in WORDS … ] ,此时使用位置参数变量 in “$@”
for 循环列表生成方式:
- 直接给出列表
- 整数列表:如 {start…end}
- 返回列表的命令:如 $(COMMAND)
- 使用glob,如:*.sh *
- 变量引用,如: @ , @, @,*,$#
双小括号方法,即((…))格式,也可以用于算术运算,双小括号方法也可以使bash Shell实现C语言风格的 变量操作:
for (( exp1; exp2; exp3 )); do COMMANDS; done
for ((控制变量初始化;条件判断表达式;控制变量的修正表达式))
do
循环体
done
示例
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
说明:
控制变量初始化:仅在运行到循环代码段时执行一次
控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断
循环 while
while CONDITION; do COMMANDS; done
while CONDITION; do
循环体
done
while CONDITION
do
循环体
done
说明:
CONDITION:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为 “true”,则执行一次循环;直到条件测试状态为“false”终止循环,因此:CONDTION一般应该有循环控 制变量;而此变量的值会在循环体不断地被修正
进入条件:CONDITION为 true
退出条件:CONDITION为 false
无限循环
while true; do
循环体
done
while : ; do
循环体
done
while 特殊用法 while read
while 循环的特殊用法,遍历文件或文本的每一行
while read line; do
循环体
done < /PATH/FROM/SOMEFILE
说明:依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line
循环 until
until CONDITION; do COMMANDS; done
until CONDITION; do
循环体
done
说明:
进入条件: CONDITION 为false
退出条件: CONDITION 为true
无限循环
until false; do
循环体
done
循环控制语句 continue
continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层
while CONDITION1; do
CMD1
...
if CONDITION2; then
continue
fi
CMDn
...
done
循环控制语句 break
break [N]:提前结束第N层整个循环,最内层为第1层
while CONDITION1; do
CMD1
...
if CONDITION2; then
break
fi
CMDn
...
done
循环控制 shift 命令
shift [n] 用于将参量列表 list 左移指定次数,缺省为左移一次。
参量列表 list 一旦被移动,最左端的那个参数就从列表中删除。while 循环遍历位置参量列表时,常用到 shift
示例:
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 g
e f g
f g
g
循环与菜单 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,此时使用位置参量
函数 function
函数介绍
函数 function
是由若干条shell命令组成的语句块,实现代码重用和模块化编程
它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部 分
函数和shell程序区别
- Shell程序在子Shell中运行
- 函数在当前Shell中运行。因此在当前Shell中,函数可对shell中变量进行修改
管理函数
函数由两部分组成:函数名和函数体
定义函数
#语法一
func_name(){
...函数体...
}
#语法二
function func_name {
...函数体...
}
#语法三
function func_name(){
...函数体...
}
查看函数
#查看当前已定义的函数名
declare -F
#查看当前已定义的函数定义
declare -f
#查看指定当前已定义的函数名
declare -f func_name
#查看当前已定义的函数名定义
declare -F func_name
删除函数
unset func_name
函数调用
函数的调用方式
- 可在交互式环境下定义函数
- 可将函数放在脚本文件中作为它的一部分
- 可放在只包含函数的单独文件中
调用:函数只有被调用才会执行,通过给定函数名调用函数,函数名出现的地方,会被自动替换为函数 代码
函数的生命周期:被调用时创建,返回时终止
交互式环境调用函数
交互式环境下定义和使用函数
#定义
[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首次发现它后才能使用,调用 函数仅使用其函数名即可
使用函数文件
可以将经常使用的函数存入一个单独的函数文件,然后将函数文件载入shell,再进行调用函数 函数文件名可任意选取,但最好与相关任务有某种联系,例如:functions
一旦函数文件载入shell,就可以在命令行或脚本中调用函数。
可以使用delcare -f 或set 命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数 若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入此文件
实现函数文件的过程:
- 创建函数文件,只存放函数的定义
- 在shell脚本或交互式shell中加载函数文件
- 调用函数
函数返回值
函数默认返回值是函数体中最后一条命令的退出状态码;
也可以使用return 自定义函数的返回值,在函数中使用 return,return 之后的语句将不会被执行
return 的用法:
return 语句 | 返回值 |
---|---|
return | 由return 语句的前一行命令执行结果决定 |
return 0 | 无错误返回 |
return 1-255 | 有错误返回 |
环境函数
类拟于环境变量,也可以定义环境函数,使子进程也可使用父进程定义的函数
定义环境函数:
export -f function_name
declare -xf function_name
查看环境函数:
export -f
declare -xf
函数参数
函数可以接受参数:
- 传递参数给函数:在函数名后面以空白分隔给定参数列表即可,如:testfunc arg1 arg2 …
- 在函数体中当中,可使用$1, 2 , . . . 调 用 这 些 参 数 ; 还 可 以 使 用 2, ...调用这些参数;还可以使用 2,...调用这些参数;还可以使用@, $*, $#等特殊变量
函数变量
变量作用域:
变量类型 | 特点 |
---|---|
普通变量 | 只在当前shell进程中有效,函数外定义,可以在函数内修改 |
环境变量 | 当前shell和子shell有效 |
本地变量 | 作用域在函数内,函数结束会被自动销毁 |
在函数中定义本地变量
local NAME=VALUE
#示例:本地变量只作用在函数内
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
函数递归
函数递归:
函数直接或间接调用自身,注意递归层数,可能会陷入死循环
递归特点:
- 函数内部自已调用自已
- 必须有结束函数的出口语句,防止死循环
阶乘
阶乘是基斯顿·卡曼(Christian Kramp,1760~1826)于 1808 年发明的运算符号,是数学术语。
一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1。自然数n的阶乘 写作n!
阶乘公式
n!=1×2×3×…×(n-1)×n
0!=1, n!=(n-1)!×n
用递归实现阶乘
fac(){
if [ $1 -gt 1 ];then
echo $[$1 * $(fac $[$1-1])]
else
echo 1
fi
}
fac $1
斐波拉契数列
斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”
指的是这样一个数列:1、1、2、3、5、8、13、21、34、……
在数学上,斐波那契数列以如下被以递推的方法定义
F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)
用递归求斐波拉契数列第N项的值
fib(){
if [ $1 -gt 1 ];then
echo $[ $(fib $[$1-1]) + $(fib $[$1-2]) ]
else
echo $1
fi
}
fib $1
逻辑炸弹
fork 炸弹是一种恶意程序,它的内部是一个不断在 fork 进程的无限循环,实质是一个简单的递归程序。 由于程序是递归的,如果没有任何限制,这会导致这个简单的程序迅速耗尽系统里面的所有资源
:(){ :|:& };:
bomb(){ bomb | bomb & };bomb
其它脚本相关工具
信号捕捉 trap
trap 命令可以捕捉信号,修改信号原来的功能,实现自定义功能
在脚本或程序的执行过程中,我们可以通过发送信号的方式,打断或终止程序的执行过程,为了避免这 种情况,我们可以使用信号捕捉,来自定义信号处理。
trap [-lp] [[arg] signal_spec ...]
#常用选项
-l #显示所有信号
-p #显示所有自定义的信号
trap 'command' signal #自定义指定信号的处理方式
trap '' signal #忽略指定的信号
trap '-' signal #恢复信号默认操作
trap func EXIT #退出时执行func
#查看所有信号
trap -l
#信号的三种表示方法
3) SIGQUIT
3 #信号ID
SIGQUIT #完整写法,大小写都支持
QUIT #简短写法,大小写都支持
创建临时文件 mktemp
mktemp 命令用于创建并显示临时文件,可避免冲突
mktemp [OPTION]... [TEMPLATE]
#常用选项
-d|--directory #创建目录
-u|--dry-run #只输出命令,不执行
-p DIR|--tmpdir[=DIR] #指明临时文件所存放目录位置
-t #将文件保存到$TMPDIR 定义的目录中,如果该变量未定义,则保存到/tmp 目录中
安装复制文件 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 #指定目录,如果不存在就创建
数组 array
数组介绍
变量:存储单个元素的内存空间
数组:存储多个元素的连续的内存空间,相当于多个变量的集合
数组名和索引
索引的编号从0开始,属于数值索引
索引可支持使用自定义的格式,而不仅是数值格式,即为关联索引,bash 4.0版本之后开始支持 bash的数组支持稀疏格式(索引不连续)
声明数组
#普通数组可以不事先声明,直接使用
declare -a ARRAY_NAME
#关联数组必须先声明,再使用
declare -A ARRAY_NAME
注意:两者不可相互转换
数组赋值
一次只赋值一个元素
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
显示所有数组
declare -a
引用数组
#如果省略[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]
引用数组所有元素
${ARRAY_NAME[*]}
${ARRAY_NAME[@]}
数组的长度,即数组中元素的个数(多加一个#号)
${#ARRAY_NAME[*]}
${#ARRAY_NAME[@]}
数组的所有下标(多一个!号)
${!ARRAY_NAME[*]}
${!ARRAY_NAME[@]}
删除数组
删除数组中的某元素,会导致稀疏格式
unset ARRAY[INDEX]
#删除整个数组
unset ARRAY
数组数据处理
数组切片
${ARRAY[@]:offset:number}
${ARRAY[*]:offset:number}
offset #要跳过的元素个数
number #要取出的元素个数
#取偏移量之后的所有元素
{ARRAY[@]:offset}
{ARRAY[*]:offset}
${ARRAY[*]::number} #取数组中最前面的 number 个元素
向数组中追加元素
ARRAY[${#ARRAY[*]}]=value
ARRAY[${#ARRAY[@]}]=value
关联数组
关联数组与普通数组区别:
- 关联数组要先声明,才能使用,普通数组可以不用声明
- 关联数组可以自定义下标,普通数组必须用数字
declare -A ARRAY_NAME
ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2‘...)
字符串处理
字符串切片
基于偏移量取字符串
${#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
基于模式取子串
#其中word可以是指定的任意字符,自左而右,查找var变量所存储的字符串中,第一次出现的word, 删除字符串开头至第一次出现word字符串(含)之间的所有字符,即懒惰模式,以第一个word为界删左留右
${var#*word}
#从var变量的值中删除以word开头的部分
${var#word}
#同上,贪婪模式,不同的是,删除的是字符串开头至最后一次由word指定的字符之间的所有内容,即贪婪模式,以最后一个word为界删左留右
${var##*word}
${var##word}
查找替换
#查找var所表示的字符串中,第一次 被pattern所匹配到的字符串,以substr替换之,懒惰模式
${var/pattern/substr}
#查找var所表示的字符串中,所有 能被pattern所匹配到的字符串,以substr替换之,贪婪模式
${var//pattern/substr}
#查找var所表示的字符串中,行首 被pattern所匹配到的字符串,以substr替换之
${var/#pattern/substr}
#查找var所表示的字符串中,行尾 被pattern所匹配到的字符串,以substr替换之
${var/%pattern/substr}
查找并删除
#删除var表示的字符串中第一次 被pattern匹配到的字符串,懒惰模式
${var/pattern}
#删除var表示的字符串中所有 被pattern匹配到的字符串,贪婪模式
${var//pattern}
#删除var表示的字符串中所有以pattern为行首 匹配到的字符串
${var/#pattern}
#删除var所表示的字符串中所有以pattern为行尾 所匹配到的字符串
${var/%pattern}
字符大小写转换
#把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
变量扩展
#扩展以所有prefix开头的变量
${!prefix*}
${!prefix@}
高级变量
高级变量赋值
$str 为变量名,expr 为具体字符串 这些组合可以省掉一些 if,else 的判断代码
变量配置方式 | str没有配置 | str为空字符串 | str己配置为非空字符串 |
---|---|---|---|
var=${str-expr} | var=expr | var= | var=$str |
var=${str:-expr} | var=expr | var=expr | var=$str |
var=${str+expr} | var= | var=expr | var=expr |
var=${str:+expr} | var= | var= | var=expr |
var=${str=expr} | str=expr; var=expr | str不变; var= | str不变; var=$str |
var=${str:=expr} | str=expr; var=expr | str=expr; var=expr | str不变; var=$str |
var=${str?expr} | expr 输出至 stderr | var= | var=$str |
var=${str:?expr} | expr 输出至 stderr | expr输出至stderr | var=$str |
高级变量用法-有类型变量
Shell变量一般是无类型的,但是bash Shell提供了declare和typeset两个命令用于指定变量的类型,两 个命令是等价的
declare [-aAfFgilnrtux] [-p] [name[=value] ...]
#选项:
-f #显示已定义的所有函数名及其内容
-F #仅显示已定义的所有函数名
-p #显示每个变量的属性和值
-a #声明或显示定义为数组的变量
-A #将变量定义为关联数组
-i #声明或显示定义为整型的变量
-l #声明或显示定义为小写的变量接上一部分shell脚本编程基础
**使用read命令来接受输入**
使用read来把输入值分配给一个或多个shell变量,read从标准输入中读取值,给每个单词分配一个变 量,所有剩余单词都被分配给最后一个变量,如果变量名没有指定,默认标准输入的值赋值给系统内置 变量REPLY
read [options] [name …]
#常见选项
-p #指定要显示的提示
-s #静默输入,一般用于密码
-n N #指定输入的字符长度N
-d ‘CHAR’ #输入结束符
-t N #TIMEOUT为N秒
## bash shell 的配置文件
bash shell的配置文件很多,可以分成下面类别
### 按生效范围划分两类
全局配置:针对所有用户皆有效
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc ------------------------ /etc/bash.bashrc #ubuntu
个人配置:只针对特定用户有效
~/.bash_profile
~/.bashrc
### shell登录两种方式分类
**交互式登录**
- 直接通过终端输入账号密码登录
- 使用 su - UserName 切换的用户
**配置文件生效和执行顺序:**
#放在每个文件最前
/etc/profile
/etc/profile.d/.sh
/etc/bashrc
~/ .bash_ profile
~/ .bashrc
/etc/bashrc #此文件执行两次
#放在每个文件最后
/etc/profile.d/.sh
/etc/bashrc
/etc/profile
/etc/bashrc #此文件执行两次
~/.bashrc
~/.bash_profile
注意:文件之间的调用关系,写在同一个文件的不同位置,将影响文件的执行顺序
**非交互式登录**
- su UserName
- 图形界面下打开的终端
- 执行脚本
- 任何其它的bash实例
执行顺序:
/etc/profile.d/*.sh
/etc/bashrc
~/.bashrc
**按功能划分分类**
| 内容 | Profile类 | Bashrc类 |
| ---------------------- | --------------------------------- | --------------------------------- |
| 交互式登录shell配置 | 是 | 否 |
| 非交互式登录shell配 置 | 是 | 是 |
| 全局配置文件 | /etc/profile,/etc/profile.d/*.sh | /etc/bashrc |
| 个人配置文件 | ~/.bash_profile | ~/.bashrc |
| 功能作用 | 定义环境变量,运行命令或脚本 | 定义命令别名和函数,定义本地 变量 |
### 编辑配置文件生效
修改profile和bashrc文件后需生效两种方法:
- 重新启动shell进程
- source|. 配置文件
注意:source 会在当前shell中执行脚本,所有一般只用于执行配置文件,或在脚本中调用另一个脚本的 场景
### Bash 退出任务
保存在~/.bash_logout文件中(用户),在退出登录shell时运行
功能:
- 创建自动备份
- 清除临时文件
## 流程控制
### 条件选择
#### 选择执行 if 语句
if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]… [ 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
;
i
f
[
"
1; if [ "
1;if["str" = “abc” ]; then
echo “is abc”;
elif [ “
s
t
r
"
=
"
d
e
f
"
]
;
t
h
e
n
e
c
h
o
"
i
s
d
e
f
"
;
e
l
i
f
[
"
str" = "def" ]; then echo "is def"; elif [ "
str"="def"];thenecho"isdef";elif["str” = “xyz” ]; then
echo “is xyz”;
else
echo “not abc,not def”
fi
说明:
- 多个条件时,逐个条件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if语句
- if 语句可嵌套
str1=$1
str2=
2
i
f
[
"
2 if [ "
2if["str1" = “abc” ];then
echo “str1 is abc”;
if [ “$str2” = “def” ];then
echo “str2 is def”;
fi
else
echo “str1 is not abc”;
fi
#### 条件判断 case 语句
case WORD in [PATTERN [| PATTERN]…) COMMANDS ;;]… esac
case 变量引用 in
PAT1)
分支1
;;
PAT2)
分支2
;;
…
*)
默认分支
;;
esac
#case支持glob风格的通配符
- #任意长度任意字符
? #任意单个字符
[] #指定范围内的任意单个字符
| #或者,a|b
### 循环
#### 循环执行介绍
将某代码段重复运行多次,通常有进入循环的条件和退出循环的条件
重复运行次数
- 循环次数事先已知
- 循环次数事先未知
常见的循环的命令:for, while, until
程序先进行语句判断,如真则执行循环语句,然后再进行语句判断,直至语句判断失败才跳出;
#### 循环 for
for NAME [in WORDS … ] ; do COMMANDS; done
#方式1
for 变量名 in 列表;do
循环体
done
#方式2
for 变量名 in 列表
do
循环体
done
执行机制:
- 依次将列表中的元素赋值给“变量名”;每次赋值后即执行一次循环体;直到列表中的元素耗尽,循 环结束
- 如果省略 [in WORDS ... ] ,此时使用位置参数变量 in "$@"
for 循环列表生成方式:
- 直接给出列表
- 整数列表:如 {start..end}
- 返回列表的命令:如 $(COMMAND)
- 使用glob,如:*.sh *
- 变量引用,如:$@,$*,$#
双小括号方法,即((…))格式,也可以用于算术运算,双小括号方法也可以使bash Shell实现C语言风格的 变量操作:
for (( exp1; exp2; exp3 )); do COMMANDS; done
for ((控制变量初始化;条件判断表达式;控制变量的修正表达式))
do
循环体
done
示例
for((sum=0,i=1;i<=100;i++));do
let sum+=i
done
echo sum=
s
u
m
f
o
r
(
(
s
u
m
=
0
,
i
=
1
;
i
<
=
100
;
s
u
m
+
=
i
,
i
+
+
)
)
;
d
o
t
r
u
e
d
o
n
e
e
c
h
o
s
u
m
=
sum for((sum=0,i=1;i<=100;sum+=i,i++));do true done echo sum=
sumfor((sum=0,i=1;i<=100;sum+=i,i++));dotruedoneechosum=sum
[root@ubuntu1804 ~]#bash sum.sh
sum=5050
sum=5050
说明:
控制变量初始化:仅在运行到循环代码段时执行一次
控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断
#### 循环 while
while CONDITION; do COMMANDS; done
while CONDITION; do
循环体
done
while CONDITION
do
循环体
done
说明:
CONDITION:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为 “true”,则执行一次循环;直到条件测试状态为“false”终止循环,因此:CONDTION一般应该有循环控 制变量;而此变量的值会在循环体不断地被修正
进入条件:CONDITION为 true
退出条件:CONDITION为 false
**无限循环**
while true; do
循环体
done
while : ; do
循环体
done
##### while 特殊用法 while read
while 循环的特殊用法,遍历文件或文本的每一行
while read line; do
循环体
done < /PATH/FROM/SOMEFILE
说明:依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line
#### 循环 until
until CONDITION; do COMMANDS; done
until CONDITION; do
循环体
done
说明:
进入条件: CONDITION 为false
退出条件: CONDITION 为true
**无限循环**
until false; do
循环体
done
#### 循环控制语句 continue
continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层
while CONDITION1; do
CMD1
…
if CONDITION2; then
continue
fi
CMDn
…
done
#### 循环控制语句 break
break [N]:提前结束第N层整个循环,最内层为第1层
while CONDITION1; do
CMD1
…
if CONDITION2; then
break
fi
CMDn
…
done
#### 循环控制 shift 命令
shift [n] 用于将参量列表 list 左移指定次数,缺省为左移一次。
参量列表 list 一旦被移动,最左端的那个参数就从列表中删除。while 循环遍历位置参量列表时,常用到 shift
示例:
until [ -z "
1
"
]
;
d
o
e
c
h
o
"
1" ]; do echo "
1"];doecho"@ "
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 g
e f g
f g
g
#### 循环与菜单 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,此时使用位置参量
## 函数 function
**函数介绍**
函数 function
是由若干条shell命令组成的语句块,实现代码重用和模块化编程
它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部 分
函数和shell程序区别
- Shell程序在子Shell中运行
- 函数在当前Shell中运行。因此在当前Shell中,函数可对shell中变量进行修改
### 管理函数
函数由两部分组成:函数名和函数体
#### 定义函数
#语法一
func_name(){
…函数体…
}
#语法二
function func_name {
…函数体…
}
#语法三
function func_name(){
…函数体…
}
#### 查看函数
#查看当前已定义的函数名
declare -F
#查看当前已定义的函数定义
declare -f
#查看指定当前已定义的函数名
declare -f func_name
#查看当前已定义的函数名定义
declare -F func_name
#### 删除函数
unset func_name
### 函数调用
函数的调用方式
- 可在交互式环境下定义函数
- 可将函数放在脚本文件中作为它的一部分
- 可放在只包含函数的单独文件中
调用:函数只有被调用才会执行,通过给定函数名调用函数,函数名出现的地方,会被自动替换为函数 代码
函数的生命周期:被调用时创建,返回时终止
#### 交互式环境调用函数
交互式环境下定义和使用函数
#定义
[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首次发现它后才能使用,调用 函数仅使用其函数名即可
#### 使用函数文件
可以将经常使用的函数存入一个单独的函数文件,然后将函数文件载入shell,再进行调用函数 函数文件名可任意选取,但最好与相关任务有某种联系,例如:functions
一旦函数文件载入shell,就可以在命令行或脚本中调用函数。
可以使用delcare -f 或set 命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数 若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入此文件
实现函数文件的过程:
1. 创建函数文件,只存放函数的定义
2. 在shell脚本或交互式shell中加载函数文件
3. 调用函数
### 函数返回值
函数默认返回值是函数体中最后一条命令的退出状态码;
也可以使用return 自定义函数的返回值,在函数中使用 return,return 之后的语句将不会被执行
return 的用法:
| return 语句 | 返回值 |
| ------------ | ------------------------------------- |
| return | 由return 语句的前一行命令执行结果决定 |
| return 0 | 无错误返回 |
| return 1-255 | 有错误返回 |
### 环境函数
类拟于环境变量,也可以定义环境函数,使子进程也可使用父进程定义的函数
定义环境函数:
export -f function_name
declare -xf function_name
查看环境函数:
export -f
declare -xf
### 函数参数
函数可以接受参数:
- 传递参数给函数:在函数名后面以空白分隔给定参数列表即可,如:testfunc arg1 arg2 ...
- 在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用$@, $*, $#等特殊变量
### 函数变量
变量作用域:
| 变量类型 | 特点 |
| -------- | ----------------------------------------------------- |
| 普通变量 | 只在当前shell进程中有效,函数外定义,可以在函数内修改 |
| 环境变量 | 当前shell和子shell有效 |
| 本地变量 | 作用域在函数内,函数结束会被自动销毁 |
在函数中定义本地变量
local NAME=VALUE
#示例:本地变量只作用在函数内
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
### 函数递归
函数递归:
函数直接或间接调用自身,注意递归层数,可能会陷入死循环
递归特点:
- 函数内部自已调用自已
- 必须有结束函数的出口语句,防止死循环
**阶乘**
阶乘是基斯顿·卡曼(Christian Kramp,1760~1826)于 1808 年发明的运算符号,是数学术语。
一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1。自然数n的阶乘 写作n!
阶乘公式
n!=1×2×3×...×(n-1)×n
0!=1, n!=(n-1)!×n
用递归实现阶乘
fac(){
if [ $1 -gt 1 ];then
echo $[$1 * $(fac $[$1-1])]
else
echo 1
fi
}
fac $1
**斐波拉契数列**
斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”
指的是这样一个数列:1、1、2、3、5、8、13、21、34、……
在数学上,斐波那契数列以如下被以递推的方法定义
F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)
用递归求斐波拉契数列第N项的值
fib(){
if [ $1 -gt 1 ];then
echo $[ $(fib $[$1-1]) + $(fib $[$1-2]) ]
else
echo $1
fi
}
fib $1
**逻辑炸弹**
fork 炸弹是一种恶意程序,它的内部是一个不断在 fork 进程的无限循环,实质是一个简单的递归程序。 由于程序是递归的,如果没有任何限制,这会导致这个简单的程序迅速耗尽系统里面的所有资源
😦){ 😐:& };:
bomb(){ bomb | bomb & };bomb
## 其它脚本相关工具
### 信号捕捉 trap
trap 命令可以捕捉信号,修改信号原来的功能,实现自定义功能
在脚本或程序的执行过程中,我们可以通过发送信号的方式,打断或终止程序的执行过程,为了避免这 种情况,我们可以使用信号捕捉,来自定义信号处理。
trap [-lp] [[arg] signal_spec …]
#常用选项
-l #显示所有信号
-p #显示所有自定义的信号
trap ‘command’ signal #自定义指定信号的处理方式
trap ‘’ signal #忽略指定的信号
trap ‘-’ signal #恢复信号默认操作
trap func EXIT #退出时执行func
#查看所有信号
trap -l
#信号的三种表示方法
3) SIGQUIT
3 #信号ID
SIGQUIT #完整写法,大小写都支持
QUIT #简短写法,大小写都支持
### 创建临时文件 mktemp
mktemp 命令用于创建并显示临时文件,可避免冲突
mktemp [OPTION]… [TEMPLATE]
#常用选项
-d|–directory #创建目录
-u|–dry-run #只输出命令,不执行
-p DIR|–tmpdir[=DIR] #指明临时文件所存放目录位置
-t #将文件保存到$TMPDIR 定义的目录中,如果该变量未定义,则保存到/tmp 目录中
### 安装复制文件 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 #指定目录,如果不存在就创建
## 数组 array
### 数组介绍
变量:存储单个元素的内存空间
数组:存储多个元素的连续的内存空间,相当于多个变量的集合
数组名和索引
索引的编号从0开始,属于数值索引
索引可支持使用自定义的格式,而不仅是数值格式,即为关联索引,bash 4.0版本之后开始支持 bash的数组支持稀疏格式(索引不连续)
### 声明数组
#普通数组可以不事先声明,直接使用
declare -a ARRAY_NAME
#关联数组必须先声明,再使用
declare -A ARRAY_NAME
注意:两者不可相互转换
### 数组赋值
一次只赋值一个元素
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
### 显示所有数组
declare -a
### 引用数组
#如果省略[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]
**引用数组所有元素**
${ARRAY_NAME[*]}
${ARRAY_NAME[@]}
**数组的长度,即数组中元素的个数**(多加一个#号)
${#ARRAY_NAME[*]}
${#ARRAY_NAME[@]}
**数组的所有下标**(多一个!号)
${!ARRAY_NAME[*]}
${!ARRAY_NAME[@]}
### 删除数组
删除数组中的某元素,会导致稀疏格式
unset ARRAY[INDEX]
#删除整个数组
unset ARRAY
### 数组数据处理
**数组切片**
${ARRAY[@]:offset:number}
${ARRAY[*]:offset:number}
offset #要跳过的元素个数
number #要取出的元素个数
#取偏移量之后的所有元素
{ARRAY[@]:offset}
{ARRAY[*]:offset}
${ARRAY[*]::number} #取数组中最前面的 number 个元素
**向数组中追加元素**
ARRAY[KaTeX parse error: Expected '}', got '#' at position 2: {#̲ARRAY[*]}]=valu…{#ARRAY[@]}]=value
### 关联数组
关联数组与普通数组区别:
- 关联数组要**先声明**,才能使用,普通数组可以不用声明
- 关联数组可以自定义下标,普通数组必须用数字
declare -A ARRAY_NAME
ARRAY_NAME=([idx_name1]=‘val1’ [idx_name2]='val2‘…)
## 字符串处理
### 字符串切片
#### 基于偏移量取字符串
${#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
#### 基于模式取子串
#其中word可以是指定的任意字符,自左而右,查找var变量所存储的字符串中,第一次出现的word, 删除字符串开头至第一次出现word字符串(含)之间的所有字符,即懒惰模式,以第一个word为界删左留右
${var#*word}
#从var变量的值中删除以word开头的部分
${var#word}
#同上,贪婪模式,不同的是,删除的是字符串开头至最后一次由word指定的字符之间的所有内容,即贪婪模式,以最后一个word为界删左留右
${var##*word}
${var##word}
### 查找替换
#查找var所表示的字符串中,第一次 被pattern所匹配到的字符串,以substr替换之,懒惰模式
${var/pattern/substr}
#查找var所表示的字符串中,所有 能被pattern所匹配到的字符串,以substr替换之,贪婪模式
${var//pattern/substr}
#查找var所表示的字符串中,行首 被pattern所匹配到的字符串,以substr替换之
${var/#pattern/substr}
#查找var所表示的字符串中,行尾 被pattern所匹配到的字符串,以substr替换之
${var/%pattern/substr}
### 查找并删除
#删除var表示的字符串中第一次 被pattern匹配到的字符串,懒惰模式
${var/pattern}
#删除var表示的字符串中所有 被pattern匹配到的字符串,贪婪模式
${var//pattern}
#删除var表示的字符串中所有以pattern为行首 匹配到的字符串
${var/#pattern}
#删除var所表示的字符串中所有以pattern为行尾 所匹配到的字符串
${var/%pattern}
### 字符大小写转换
#把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
### 变量扩展
#扩展以所有prefix开头的变量
${!prefix*}
${!prefix@}
## 高级变量
### 高级变量赋值
$str 为变量名,expr 为具体字符串 这些组合可以省掉一些 if,else 的判断代码
| 变量配置方式 | str没有配置 | str为空字符串 | str己配置为非空字符串 |
| ---------------- | ------------------ | ------------------ | --------------------- |
| var=${str-expr} | var=expr | var= | var=$str |
| var=${str:-expr} | var=expr | var=expr | var=$str |
| var=${str+expr} | var= | var=expr | var=expr |
| var=${str:+expr} | var= | var= | var=expr |
| var=${str=expr} | str=expr; var=expr | str不变; var= | str不变; var=$str |
| var=${str:=expr} | str=expr; var=expr | str=expr; var=expr | str不变; var=$str |
| var=${str?expr} | expr 输出至 stderr | var= | var=$str |
| var=${str:?expr} | expr 输出至 stderr | expr输出至stderr | var=$str |
### 高级变量用法-有类型变量
Shell变量一般是无类型的,但是bash Shell提供了declare和typeset两个命令用于指定变量的类型,两 个命令是等价的
declare [-aAfFgilnrtux] [-p] [name[=value] …]
#选项:
-f #显示已定义的所有函数名及其内容
-F #仅显示已定义的所有函数名
-p #显示每个变量的属性和值
-a #声明或显示定义为数组的变量
-A #将变量定义为关联数组
-i #声明或显示定义为整型的变量
-l #声明或显示定义为小写的变量
-n #变量引用另外一个变量的值
-r #声明或显示只读变量
-t #声明或显示具有trace(追踪)属性的变量
-u #声明或显示定义为大写的变量
-x #显示环境变量和函数,相当于export
### 变量间接引用
#### eval命令
eval命令将会首先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于那些一次扫描无法实 现其功能的变量,该命令对变量进行两次扫描
示例
[root@ubuntu2204 ~]# CMD=whoami
[root@ubuntu2204 ~]# echo $CMD
whoami
[root@ubuntu2204 ~]# eval KaTeX parse error: Expected 'EOF', got '#' at position 29: …t@ubuntu2204 ~]#̲ n1=6 [root@ubu…n1}
{1…6}
#两次展开
[root@ubuntu2204 ~]# eval echo {1…$n1}
1 2 3 4 5 6
#### 间接变量引用
如果第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值就称为间接变量引用 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 $
v
a
r
i
a
b
l
e
1
e
v
a
l
e
c
h
o
′
variable1 eval echo '
variable1evalecho′’$variable1
echo $tmpvar
#方法2
#变量赋值
tempvar=${!variable1}
#显示值
echo ${!variable1}
#示例
[root@ubuntu2204 ~]# ceo=name
[root@ubuntu2204 ~]# name=123
[root@ubuntu2204 ~]# eval echo $$ceo
123
-n #变量引用另外一个变量的值
-r #声明或显示只读变量
-t #声明或显示具有trace(追踪)属性的变量
-u #声明或显示定义为大写的变量
-x #显示环境变量和函数,相当于export
变量间接引用
eval命令
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
间接变量引用
如果第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值就称为间接变量引用 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=123
[root@ubuntu2204 ~]# eval echo \$$ceo
123