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中删除函数。改动完毕后,再重新载入此文件

实现函数文件的过程:

  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 , . . . 调 用 这 些 参 数 ; 还 可 以 使 用 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=exprvar=var=$str
var=${str:-expr}var=exprvar=exprvar=$str
var=${str+expr}var=var=exprvar=expr
var=${str:+expr}var=var=var=expr
var=${str=expr}str=expr; var=exprstr不变; var=str不变; var=$str
var=${str:=expr}str=expr; var=exprstr=expr; var=exprstr不变; var=$str
var=${str?expr}expr 输出至 stderrvar=var=$str
var=${str:?expr}expr 输出至 stderrexpr输出至stderrvar=$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
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值