1.shell脚本语言
1.1基本结构
-
shell是基于过程式、解释执行的语言
-
shell脚本是包含一些命令或声明,并符合一定格式的文本文件
-
首先必须符合shebang机制的格式要求:
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
#!/usr/bin/ruby
#!/usr/bin/lua
1.2shell脚本的注释规范
一般要注明作者,创建(修改)时间,文件名,版本号,该程序的功能及作用,目地,版权信息,作者联系方式等
1.3shell脚本执行
#使用bash,是在shell脚本文件没有执行权限的情况下
$ bash test1.sh
#管道
$ cat test1.sh | bash
#重定向
$ bash < test1.sh
$ bash <<< $(cat test1.sh) #<<<,就是把其后面的内容以一个单一字符串并加上换行符,发送到命令的标准输入或者文件描述符n中
#当shell脚本文件具有执行权限,或给shell脚本增加了执行权限
#绝对路径
$ /opt/test1.sh
#相对路径,也是在子进程中执行的
$ ./test1.sh
#在当前进程中执行
$ . test1.sh
1.4shell脚本调试
#检测脚本中的语法错误,不能检测命令错误,也不会执行脚本
$ bash -n cp.sh
#逐行输出命令,并输出执行结果
$ bash -x test1.sh
#语法错误:会导致后续的命令不继续执行,可以用 bash -n 检查错误,提示的出错行数不一定是准确的
#命令错误:默认后续的命令还会继续执行,用 bash -n 无法检查出来 ,可以使用 bash -x 进行观察
#逻辑错误:只能使用 bash -x 进行观察
1.5变量
变量表示命名的内存空间,将数据放在内存空间中,通过变量名引用,获取数据数据要存在内存空间中,而内存空间又是通过内存地址来访问的,但内存地址一般是16进制的编号;为了方便使用,就有了变量名,当访问一个变量时,实际是访问其对应的内存地址的那块内存空间。
1.5.1普通变量
#变量赋值
$ name="zhangsan" #直接字串
$ name="$USER" #变量引用,双引号为弱引用,可以识别变量,单引号是强引用,里面内容是什么,输出的内容就是什么
$ name=`ll -d /etc/passwd` #命令引用
$ name=$(ll -d /etc/passwd) #命令引用
#变量引用,若要保留数据的格式,对引用的变量加上双引号("")
$ echo $name
$ echo ${name}
#显示已定义的所有变量
$ set
#删除变量
$ unset name
#普通变量的生效范围只在当前shell进程,相对于当前shell的其它shell进程,以及当前shell进程的子shell进程,普通变量都是无效的
1.5.2环境变量
#变量声明和赋值
$ export name=zhangsan
$ declare -x name=zhangsan
#显示所有环境变量
$ printenv
$ env
$ export
$ declare -x
#查看当前进程的环境变量
$ cat /proc/$$/environ | tr "\0" "\n" #$$表示当前进程的PID,字符串是以"\0"结尾,以换行"\n"对其进行替换
1.5.3只读变量
#只能声明定义,但后续不能修改和删除,即常量
#声明只读变量和赋值,退出当前进程后,设置的只读变量就会失效
$ readonly PI=3.14
$ declare -r PI=3.14
#查看声明的只读变量
$ readonly
$ declare -r
1.5.4位置变量
#bash shell中内置的变量,通过命令行传递给脚本的参数
$1,$2,... #对应第1个、第2个等参数,shift [n]换位置
$0 #命令本身,包括路径
$* #传递给脚本的所有参数,全部参数合为一个字符串
$@ #传递给脚本的所有参数,每个参数为独立字符串
$# #传递给脚本的参数的个数
#清空所有位置变量
$ set --
1.5.5退出状态码变量
#进程执行后,将使用变量 $? 保存状态码的相关数字,不同的值反应成功或失败,$?取值范例 0-255
$ echo $? #0表示命令执行成功,1-255表示执行失败
#用户可以在脚本中自定义退出状态码
exit num
#脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
#如果exit后面无数字,终止退出状态取决于exit命令前面命令执行结果
#如果没有exit命令,即未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码
1.5.6脚本安全和set
#set命令,可以用来定制 shell 环境
#查看命令行下的$-变量
$ echo $-
himBHs #h:hash表,B:大括号扩展
#关闭大括号扩展
$ set +B
#启用大括号扩展
$ set -B
#set命令实现脚本安全,+是禁止,-是开启
#显示所有
$ set -o
#开启此项,在使用一个没有声明的变量时,会报错,并且终止程序
$ set -o nounset #同set -u
#开启此项,命令返回非0就退出,不再继续执行后面的代码
$ set -o errexit #同set -e
1.6格式化输出printf
#相对于echo,有着更为丰富的输出格式
$ printf format args...
#常用的格式替换符,也就是format
%s #字符串
%d|%i #十进制整数
%u #不带正负号的十进制
%f #浮点数
%c #ASCII字符,显示对应参数的第一个字符
%b #相对应的参数中包含转义字符时,可以使用此替换符进行替换,对应的转义字符会被转义
%o #八进制
%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 #水平制表符,也就tab键
\v #垂直制表符
\ #\本身
1.7算术运算符
#括号内赋值:((变量名=整数表达式))
$ ((k=i+j));echo $k
#括号外赋值:变量名=$((整数表达式))
$ k=$((i+j));echo $k
#变量名=$[表达式]
$ k=$[m=i+j];echo $k $m
$ k=$[i+j];echo $k
#let 变量名=表达式
$ let k=i+j;echo $k
#expr 表达式:表达式中,每参数与运算符间需要有空格,特殊字符需要转义
$ expr 10 + 20
$ expr 10 \* 20
#内建的随机数生成器变量,取值范围:0-32767
$ echo $RANDOM
1.8逻辑运算
1.8.1与或非
&,|,!,^
1.8.2短路运算
&&,||
1.9条件测试命令
test expr
[ arg... ] #同 test
[[ expression ]] #增加版的[],支持[],支持扩展正则表达式和通配符
#注意:[] 中的表达式,前后要有空格
1.9.1变量测试
-v VAR #如果变量被设置为为真
-R VAR #如果变量被设置且被引用则为真
1.9.2数值测试
#数字相关 格式 arg1 OP arg2
-eq,-ne,-lt,-le,-gt,-ge
#算术表达式比较
==,!=,<=,>=,<,>
1.9.3字符串测试
#test和[]字符串测试用法
#最好将字符串放在""里面
-z STRING #判断字符串是否为空,为空则为真
-n STRING #如果字符串不为空则为真
STRING #同上
#两个字符串变量比较,一定要有空格,如果没有空格,就变成赋值了
STRING1 = STRING2 #如果 string1 和 string2 字符串相同则为真
STRING1 != STRING2 #如果 string1 和 string2 字符串不相同则为真
STRING1 < STRING2 #只比较第一个字符,以字母表顺序来比较,string1在string2 之前则为真,< 要转义
STRING1 > STRING2 #只比较第一个字符,以字母表顺序来比较,string1在string2 之后则为真, > 要转义
#[[]]字符串测试用法
== #左侧字符串是否和右侧的PATTERN相同
#注意:此表达式用于[[ ]]中,PATTERN为通配符
=~ #左侧字符串是否能够被右侧的正则表达式的PATTERN所匹配
#注意: 此表达式用于[[ ]]中为扩展的正则表达式
1.9.4文件测试
-a|-e #判断文件是否存在,存在则为真,建议使用-e
-f #如果文件存在且为常规文件则为真
-d #判断是否为目录,是目录则为真
-w|-r|-x #对文件的权限测试,以用户对文件的实际权限决定,而非文件属性决定
FILE1 -nt FILE2 #如果 file1 文件新于 file2 文件 则为真(根据修改日期)
FILE1 -ot FILE2 #如果 file1 文件旧于 file2 文件则为真
FILE1 -ef FILE2 #如果 file1 文件是 file2 文件的硬链接则为真
-s FILE #如果文件存在且不为空则为真
#通过chattr对文件的属性进行修改,也会对文件的书写权限有作用
1.10关于()和{}
-
( cmd1;cmd2;... ) 和 { cmd1;cmd2;...; } 都可以将多个命令组合在一起,批量执行
-
( list ) 会开启子shell,并且list中变量赋值及内部命令执行后,将不再影响后续的环境,但内部命令会继承括号外的父进程的变量
-
{ list; } 不会开启子shell, 在当前shell中运行,会影响当前shell环境,左侧要有空格,右侧要有;结束
1.11组合测试条件
1.11.1测试命令方式
#-a和-o 需要使用测试命令进行,[[ ]] 不支持,要注意其中的书写格式
[ EXPRESSION1 -a EXPRESSION2 ] #并且,EXPRESSION1和EXPRESSION2都是真,结果才为真
[ EXPRESSION1 -o EXPRESSION2 ] #或者,EXPRESSION1和EXPRESSION2只要有一个真,结果就为真
[ ! EXPRESSION ] #取反
1.11.2条件表达式方式
COMMAND1 && COMMAND2 #并且,短路与,代表条件性的AND THEN,如果COMMAND1成功,将执行COMMAND2,否则,将不执行COMMAND2
COMMAND1 || COMMAND2 #或者,短路或,代表条件性的OR ELSE,如果COMMAND1 成功,将不执行COMMAND2,否则,将执行COMMAND2
! COMMAND #非,取反
#&&和||组合使用,&& 要在前,|| 放在后
2.bash shell的配置文件
2.1按生效范围分类
2.1.1全局配置
针对所有用户都有效:
-
/etc/profile
-
/etc/profile.d/*.sh
-
/etc/bashrc ------------------------ /etc/bash.bashrc #ubuntu
2.1.2个人配置
只针对特定用户有效:
-
~/.bash_profile
-
~/.bashrc
2.2按shell登录方式分类
2.2.1交互式登录
就是使用账号密码登录或者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
2.2.2非交互式登录
或su username,或图形化界面下打开的终端,或执行脚本,或其他的bash实例,在这种情况下配置文件生效和执行顺序为:
/etc/profile.d/*.sh
/etc/bashrc
~/.bashrc
2.3按功能分类
profile类:定义环境变量,运行命令或脚本
bashrc类:定义命令别名或函数,本地变量
2.4编辑配置文件生效
修改profile和bashrc文件后要生效,有两种方法:
-
重新启动shell进程
-
source|. 配置文件
2.5bash退出任务
保存在~/.bash_logout文件中(用户),在退出登录shell时运行,可以实现在退出登录时自动备份或清理临时文件等功能
3.流程控制
3.1条件判断
3.1.1选择执行if语句
#单分支
if 判断条件;theh
条件为真时的执行语句;
fi
#双分支
if 判断条件;then
条件为真时的执行语句;
else
条件为假时的执行语句;
fi
#多分支
if 判断条件1;then
条件1为真时的执行语句;
elif 判断条件2;then
条件2为真时的执行语句;
elif 判断条件3;then
条件3为真时的执行语句;
....
else
上面条件都为假时的执行语句;
fi
3.1.2条件判断case语句
case 变量引用 in
PATH1)
分支1
;;
PATH2)
分支2
;;
....
*)
默认分支
;;
esac
#case支持*,?,|,[]等通配符
3.2循环
3.2.1循环for
#方式1
for 变量名 in 列表;do
循环体;
done
#方式2
for 变量名 in 列表
do
循环体;
done
#双小括号(())
for ((控制变量初始化;条件判断表达式;控制变量的修正表达式))
do
循环体;
done
3.2.2循环while
#格式
while 判断条件 #判断条件为真进入循环体
do
循环体;
done
#while read,依次读取文件的每一行,并赋值给line
while read line
do
循环体;
done < 文件路径
3.2.3循环until
#格式
until 判断条件 #判断条件为假进入循环体
do
循环体;
done
3.2.4循环控制语句continue
continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断,最内层为第1层
3.2.5循环控制语句break
break [N]:提前结束第N层整个循环,最内层为第1层
3.2.6循环控制shift命令
shift [n] 用于将参量列表 list 左移指定次数,缺省为左移一次。
参量列表 list 一旦被移动,最左端的那个参数就从列表中删除。
while 循环遍历位置参量列表时,常用到shift
3.2.7循环与菜单select
#格式
select NAME in list
do
循环体;
done
-
select 循环主要用于创建菜单,按数字顺序排列的菜单项显示在标准输出上,并显示 PS3 提示符,等待用户输入
-
用户输入菜单列表中的某个数字,执行相应的命令
-
用户输入菜单列表中的某个数字,会将对应的WORD值赋值给NAME变量
-
用户输入被保存在内置变量 REPLY 中
-
select 是个无限循环,因此要用 break 命令退出循环,或用 exit 命令终止脚本。也可以按 ctrl+c 退出循环
-
select 经常和 case 联合使用
-
与 for 循环类似,可以省略 in list,此时使用位置参量
4.函数function
4.1管理函数
函数是由函数名和函数体两部分组成的
4.1.1定义函数
#语法1:
func_name(){
函数体;
}
#语法2:
function func_name(){
函数何醒;
}
#语法3:
function func_name{
函数体;
}
4.1.2查看函数
#查看当前已定义的函数名
declare -F
#查看当前已定义的函数定义
declare -f
#查看指定当前已定义的函数名
declare -f func_name
#查看当前已定义的函数名定义
declare -F func_name
4.1.3删除函数
unset func_name
4.2函数调用
函数只有被调用才会执行,通过给定函数名调用函数,函数名出现的地方,会被自动替换为函数代码,函数的生命周期为被调用时创建,返回时终止
函数的调用方式
-
可在交互式环境下定义函数
-
可将函数放在脚本文件中作为它的一部分
-
可放在只包含函数的单独文件中
4.2.1交互式环境调用函数
4.2.2在脚本中定义及调用函数
函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell首次发现它后才能使用,调用函数仅使用其函数名即可
4.2.3使用函数文件
可以将经常使用的函数存入一个单独的函数文件,然后将函数文件载入shell,再进行调用函数。
函数文件载入shell,就可以在命令行或脚本中调用函数。
若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入此文件
实现函数文件的过程:
-
创建函数文件,只存放函数的定义
-
在shell脚本或交互式shell中加载函数文件
-
调用函数
#函数文件载入shell的方式
#方式1
$ . functions #functions为函数文件的名称
#方式2
$ source functions
4.3函数的返回值return
函数默认返回值是函数体中最后一条命令的退出状态码;也可以使用return 自定义函数的返回值,在函数中使用 return,return 之后的语句将不会被执行。
return的用法:
-
return---->返回值:由return 语句的前一行命令执行结果决定
-
return 0----->返回值:无错误返回
-
return 1-255----->返回值:有错误返回
return和exit的区别:return只会中断函数的执行,而exit会中止整个shell脚本的执行
4.4环境函数
类似于环境变量,也可以定义环境函数,使子进程也可使用父进程定义的函数
#定义环境函数
export -f 函数名
declare -xf 函数名
#查看环境函数
export -f
declare -xf
4.5函数参数
传递参数给函数,在函数名后面以空白分隔给定参数列表即可,如:testfunc arg1 arg2 ...
在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用$@, $*, $#等特殊变量
4.6函数变量
普通变量:只在当前shell进程中有效,函数外定义,可以在函数内修改
环境变量:当前shell和子shell有效
本地变量:作用域在函数内,函数结束会被自动销毁,且只能在函数内定义,在函数外定义会报错
#在函数内部定义本地变量
local NAME=VALUE
5.脚本相关工具
5.1信号捕捉trap
trap 命令可以捕捉信号,修改信号原来的功能,实现自定义功能。在脚本或程序的执行过程中,我们可以通过发送信号的方式,打断或终止程序的执行过程,为了避免这种情况,我们可以使用信号捕捉,来自定义信号处理。
#trap的使用
trap -l #显示所有信号
trap -p #显示所有自定义的信号
trap 'command' signal #自定义指定信号的处理方式
trap '' signal #忽略指定的信号
trap '-' signal #恢复信号默认操作
trap func EXIT #退出时执行func
#信号的三种表示方法:3) SIGQUIT
3 #信号ID
SIGQUIT #完整写法,大小写都支持
QUIT #简短写法,大小写都支持
5.2创建临时文件mktemp
#常用选项
-d|--directory #创建目录
-u|--dry-run #只输出命令,不执行
-p DIR|--tmpdir[=DIR] #指明临时文件所存放目录位置
-t #将文件保存到$TMPDIR 定义的目录中,如果该变量未定义,则保存到/tmp 目录中
5.3安装复制文件install
#install 功能相当于cp,chmod,chown,chgrp ,mkdir 等相关工具的集合
#常用选项
-m|--mode=MODE #指定权限,默认755
-v|--verbose #显示过程
-o|--owner=OWNER #指定属主
-g|--group=GROUP #指定属组
-d|--directory DIR #指定目录,如果不存在就创建
6.数组
变量是存储单个元素的内存空间,数组是存储多个元素的连续的内存空间,相当于多个变量的集合。数组索引的编号从0开始,属于数值索引,但索引也可支持使用自定义的格式,而不仅是数值格式,即为关联索引,同时,bash 4.0版本之后开始支持bash的数组支持稀疏格式(索引不连续)
6.1声明数组
#普通数组和关联数组是不可以进行相互转换的
#普通数组可以不事先声明,直接使用
declare -a ARRAY_NAME
#关联数组必须先声明,再使用
declare -A ARRAY_NAME
6.2数组赋值
#一次只赋值一个元素
ARRAY_NAME[INDEX]=VALUE
#一次赋值全部元素
ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)
#只赋值特定元素
ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)
#交互式数组赋值
read -a ARRAY
6.3显示所有数组
declare -a
6.4引用数组
#引用数组中特定的元素
#如果省略[INDEX]表示引用下标为0的元素
${ARRAY_NAME[INDEX]}
#引用数组所有元素
${ARRAY_NAME[*]}
${ARRAY_NAME[@]}
#数组的长度
${#ARRAY_NAME[*]}
${#ARRAY_NAME[@]}
#数组的所有下标
${!ARRAY_NAME[*]}
${!ARRAY_NAME[@]}
6.5删除数组
#删除数组中的元素
unset ARRAY[INDEX]
#删除整个数组
unset ARRAY
6.6数组数据处理
#从下标为offset开始(包含),取数组后面的number个元素
${ARRAY[@]:offset:number}
${ARRAY[*]:offset:number}
#从下标为offset开始(包含),取数组后面所有的元素
{ARRAY[@]:offset}
{ARRAY[*]:offset}
#取数组最前面的number个元素
${ARRAY[*]::number}
#向数组中追加元素
ARRAY[${#ARRAY[*]}]=value
ARRAY[${#ARRAY[@]}]=value
6.7关联数组
关联数组与普通数组区别:
-
关联数组要先声明,才能使用,普通数组可以不用声明
-
关联数组可以自定义下标,普通数组必须用数字
#声明关联数组,并赋值
declare -A ARRAY_NAME
ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2‘...)
7.字符串处理
7.1字符串切片
#基于偏移量取字符串
#返回字符串变量var的字符的长度,一个汉字算一个字符
${#var}
#返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,到最后的部分
${var:offset}
#返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,长度为number的部分
${var:offset:number}
#取字符串的最右侧几个字符, 注意:冒号后必须有一空白字符
${var: -length}
#从最左侧跳过offset字符,一直向右取到距离最右侧lengh个字符之前的内容,即:掐头去尾
${var:offset:-length}
#先从最右侧向左取到length个字符开始,再向右取到距离最右侧offset个字符之间的内容,注意:-length前空格,并且length必须大于offset
${var: -length:-offset}
#基于模式取子串
#从var变量的值中删除以word开头的部分
${var#word}
#其中word可以是指定的任意字符,自左而右,查找var变量所存储的字符串中,第一次出现的word, 删除字符串开头至第一次出现word字符串(含)之间的所有字符,即懒惰模式,以第一个word为界删左留右
${var#*word}
#删除的是字符串开头至最后一次由word指定的字符之间的所有内容,即贪婪模式,以最后一个word为界删左留右
${var##*word}
${var##word}
#其中word可以是指定的任意字符,功能:自右而左,查找var变量所存储的字符串中,第一次出现的word,删除字符串最后一个字符向左至第一次出现word字符串(含)之间的所有字符,即懒惰模式,以从右向左的第一个word为界删右留左
${var%word*}
${var%word}
#同上,只不过删除字符串最右侧的字符向左至最后一次出现word字符之间的所有字符,即贪婪模式,以从右向左的最后一个word为界删右留左
${var%%word*}
${var%%word}
7.2查找替换
#查找var所表示的字符串中,第一次被pattern所匹配到的字符串,以substr替换之,懒惰模式
${var/pattern/substr}
#查找var所表示的字符串中,所有能被pattern所匹配到的字符串,以substr替换之,贪婪模式
${var//pattern/substr}
#查找var所表示的字符串中,行首被pattern所匹配到的字符串,以substr替换之
${var/#pattern/substr}
#查找var所表示的字符串中,行尾被pattern所匹配到的字符串,以substr替换之
${var/%pattern/substr}
7.3查找并删除
#删除var表示的字符串中第一次被pattern匹配到的字符串,懒惰模式
${var/pattern}
#删除var表示的字符串中所有被pattern匹配到的字符串,贪婪模式
${var//pattern}
#删除var表示的字符串中所有以pattern为行首匹配到的字符串
${var/#pattern}
#删除var所表示的字符串中所有以pattern为行尾所匹配到的字符串
${var/%pattern}
7.4字符大小写转换
#把var中的所有小写字母转换为大写
${var^^}
#把var中的所有大写字母转换为小写
${var,,}
7.5变量扩展
#扩展以所有prefix开头的变量
${!prefix*}
${!prefix@}
8.高级变量
8.1高级变量赋值
$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
8.2高级变量用法-有类型变量
Shell变量一般是无类型的,但是bash Shell提供了declare和typeset两个命令用于指定变量的类型,两个命令是等价的
8.3变量间接引用
eval命令:首先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量,该命令对变量进行两次扫描
间接变量引用:如果第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值就称为间接变量引用variable1的值是variable2,而variable2又是变量名,variable2的值为value,间接变量引用是指通过variable1获得变量值value的行为
#bash Shell提供了两种格式实现间接变量引用
#方法1
#变量赋值
eval tempvar=\$$variable1
#显示值
eval echo \$$variable1
eval echo '$'$variable1
echo $tmpvar
#方法2
#变量赋值
tempvar=${!variable1}
#显示值
echo ${!variable1}

被折叠的 条评论
为什么被折叠?



