Shell(九):变量的高阶用法

发布时间 2023-10-18 15:46:37作者: 无虑的小猪

  在Shell(六):变量和引用已经介绍了变量的基本用法,包括变量的替换和赋值、无类型性、环境变量、以及四种引用符号。

1、内部变量

  内部变量是指能够对Shell脚本行为产生影响的变量,对Shell及其子Shell都有效。内部变量属于环境变量。在前面已经解介绍了 PWD、SHELL、USER、UID、PPID、PS1、PS2和IFS变量。

1.1、BASH

  BASH记录了当前bash Shell的路径,通常为/bin/bash,内部变量SHELL就是通过BASH的值确定当前Shell的类型。

0

1.2、BASH_SUBSHELL

  BASH_SUBSHELL记录了子Shell的层次,在bash版本3之后出现的。

1.3、BASH_VERSINFO

  BASH_VERSINFO是一个数组,包含6个元素,这6个元素用于表示bash的版本信息,新建var21.sh脚本,用于输出BASH_VERSINFO数组的值,脚本内容那个如下:

#!/bin/bash

for n in 0 1 2 3 4 5
do
    echo "BASH_VERSINFO[$n]=${BASH_VERSINFO[$n]}"
done

  执行结果如下:

0

  下标0的内容表示 bash Shell的主版本号,下标1的内容表示bash Shell的次版本号,下标2的内容表示 bash Shell的补丁级别,下标3的内容表示 bash Shell 的编译版本,下标4表示bash Shell的发型状态,下标5表示bash Shell的硬件架构。

1.4、GROUPS

  GROUPS记录了当前用户所属的群组,Linux的一个用户可同时包含在多个组内,GROUPS是一个数组,数组记录了当前用户所属的群组号。Linux管理用户组的文件时/etc/group,每个群组对应该文件中的一行,并用冒号分成四个域.

0

1.5、HOSTNAME

  HOSTNAME记录了主机名,Linux主机名是网络配置时必须要设置的参数,一般在/etc/sysconfig/network文件中设置主机名。/etc/hosts文件用于设定IP地址和主机名之间的对应关系,以利于快速从主机名查找到IP地址。

1.6、OSTYPE

  OSTYPE记录了操作系统类型,Linux系统中,$OSTYPE=linux。

0

1.7、REPLY

  REPLY变量与read和select命令有关,read命令用于读取标准输入(stdin)的变量值,read命令的一般格式为:

read variable

  variable是变量名,read将读到的标准输入存储到variable变量中。read命令也可以不带任何变量名,此时,read就将读到的标准输入存储到REPLY变量中。

  新建readreply.sh脚本,详情如下:

#!/bin/bash
# 第一部分
echo -n "What is your name?"
read        # read不带变量名
echo "Your name is $REPLY"
    # 打印 $REPLY
# 第二部分 
echo -n "What is the name of your father?"
read fname
echo "Your father's name is $fname"
echo "But the \$REPLY is $REPLY"

  readreply.sh脚本分为两部分,第一部分用不带变量名的read命令接受用户输入,然后打印REPLY变量值,执行结果如下:

0

  用户输入JJ,$REPLY=JJ,说明read命令读到的值确实存储到REPLY变量中;第二部分read命令后跟fname变量,然后分别打印fname和REPLY的值,从readreply.sh脚本的执行结果可知,$fname=kk,说明此事read命令读到的值存储到了fname变量中,而REPLY的值保持不变,仍为JJ。

  bash Shell的select命令源自于Korn Shell,是一种建立菜单的工具,它提供一组字符串供用户选择,用户不必完整的输入字符串,只需输入相应的序号进行选择,select命令的格式如下:

select variable in list do ... break done

  select命令格式中的list是字符串列表,select自动将list形成有编号的菜单,用户输入序号以后,将该序号所对应的list中的字符串赋给variable变量,而序号值则保存到REPLY变量中。

  select命令的do break语句段中可以添加Shell命令,对variable或REPLY进行调用。

  新建 readreply02.sh 脚本,详情如下:

#!/bin/bash

echo "Pls. choose your profession?"
select var in "Worker" "Doctor" "Teacher" "Student" "Other"
do
    echo "The \$REPLY is $REPLY"
    echo "Your preofession is $var"
    break
done

  readreply02.sh脚本中的select命令将list设置为5个字符串,每个字符串用双引号起,中间用空格分隔,select命令的变量名为var。do break语句段打印REPLY和var的值。执行结果如下:

0

  select自动将list中的字符串建成菜单,并用数字对每个字符串标号,用户选择序号,REPLY变量显示序号,var变量对应序号序号所对应的字符串Docotor。

  在执行结果中,输入select命令后,Shell提示符变为"#?",该提示符由Shell提示符变量PS3进行设置,"#?"是其默认值。

1.8、SECONDS

  SECONDS记录脚本从开始执行到结束所耗费的时间,以秒为单位。新建一个 sec.sh 的脚本,详情如下:

#!/bin/bash

# 定义两个变量:count记录循环次数;MAX为while循环条件
count=1
MAX=5

while [ "$SECONDS" -le "$MAX" ]  # 当SECOUNDS小于等于MAX时,执行循环体
do
    echo "CURRENT SECONDS IS $SECONDS"
    echo "$count time to sleep"
    let count=$count+1
    sleep 2     # 运行该脚本进程休眠2秒
done

echo "The running time of this script is $SECONDS"

  sec.sh脚本的主体是一个while循环,while循环体的执行条件是该脚本的执行时间小于等于MAX变量的值,循环体语句首先打印进入循环体的次数,然后调用sleep命令使运行该脚本进程休眠2s。该脚本共休眠3次,执行时间为6s。执行结果如下:

0

1.9、TMOUT

  TMOUT变量用于设置Shell的过期时间,当TMOUT不为0时,Shell在TMOUT秒后,将自动注销。TMOUT放在脚本中,可以规定脚本的执行时间。

  新建 timedread.sh 脚本,详情如下:

#!/bin/bash
# 执行脚本后,用户什么都不输入,等待TMOUT后,脚本自动运行结束

# 脚本执行时间是5秒
TMOUT=5
echo "What is your name?"
read fname

if [ -z "$fname" ]  # 如果fname为空
then
        echo "wait time $TMOUT seconds has not input"
        fname="(no answer)"
fi

echo "Your name is $fname"

   执行timeread.sh脚本后,不做任何输入,等待TMOUT时间后,脚本自动运行结束。

  0
  0

2、字符串处理

  bash Shell字符串处理命令归结起来有两种,第一种是awk命令,第二种是expr命令。

  expr是Linux中一个功能强大的命令,引出通用求值表达式,可以实现算术操作、比较此操作、字符串操作和逻辑操作等功能。

2.1、${#...}和 expr length

  bash Shell除了awk的length(s)函数,还有另外两种方法计算字符串的长度,假设字符串名为str,两种方法的命令如下:

${#str}
expr length $str

  演示案例如下:

0

  注意:expr length后面只能跟一个参数,若不用双引号将$str 引起,Shell认为expr length后面带了多个参数,因此当字符串中带空格时,需要用双引号引起。若字符串不包含空格,则可以不用引号引起。

0

2.2、expr index

  expr的索引命令格式为:

expr index $str $substr

  expr索引命令的概念在字符串 $str 上匹配 $substr 中第一次出现的位置,若在 $str 上匹配不到 $substr 中的任何字符,expr index 返回0。

  案例演示详情如下:

0

  第1、2个命令subtr为ell,ell在str中出现过,第一个出现的字符是e,位置是2;

  第3个命令substr为Shell,Shell在str中出现过,第一个出现的字符时e,位置是2;

  第4个命令substr为l,在str第一次出现的位置是3;

  第5个命令substr是S,在str第一次出现的位置是7;

  第6个命令substr是w,在str中没有w字符,返回0。

2.3、expr match

  expr match命令的格式为:

expr match $str $substr

  expr match 命令在str的开头匹配substr字符串,返回匹配到的substring字符串的长度,若string开头匹配不到substrinng,则返回0。substring可以是字符串,也可以是正则表达式。

0

  第1条命令,H.*表示以H开头后面跟任意字符的字符串,匹配 str 整个字符串;

  第2条命令,HEL字符串匹配长度是3;

  第3条命令,Shell在str中出现过,但是不在str的开头,仍返回0。

2.4、抽取子串

  抽取子串命令格式:

#{str:position}
#{str:position:length}

  第1种格式的命令从名称为 $str 的字符串的 $position 个位置开始抽取子串,第2中格式命令在第1中格式命令的基础上添加了$length变量,表示从名称为$str的字符串的第$position个位置开始抽取长度为$length的子串。${...}格式的命令从0开始对名称为$str的字符串进行标号。

0

  第1个命令,position为0,由于str以0开始标号,所以该命令将string的整串抽取出来作为子串;

  第2个命令position为5,即从标号为5开始抽取子串;

  第3个命令,position为2,length为8,即从标号为2的字符开始抽取长度为6的子串。

  #{...}命令还提供从str右边开始计数抽取子串的功能,格式如下:

#{str: -position}    # 冒号和横杠符号之间有一个空格符
#{str:(-position)}   # 冒号和左括号之间未必要有空格符

  详情如下:

0

2.5、删除子串

  删除子串是指经原字符串中符合条件的子串删除,删除子串命令只有#{...}格式的,但是,删除子串命令可分为从开头处删除和从结尾处删除两种不同的命令。

  开头处删除子串的命令:

${str#substr}    # 删除str开头处与substr匹配的最短子串
${str##substr}    # 删除str开头处与substr匹配的最长子串

  第1种格式的功能是删除开头处与substr匹配的最短子串,及str开头处一旦与substr匹配就立即删除;

  第2种格式命令的功能是删除开头处与substr匹配的最长子串,即str开头处直到无法再与substr匹配时才删除。

0

  第1条命令,删除str开头字符2和1之间的最短匹配,结果匹配到1116的第一个1;第2条命令删除开头字符2和1之间最长的匹配,结果匹配到1116的最后一个1,结果匹配到1116的最后一个1。两条命令中的*字符表示起始字符和终止字符之间的任意字符。

  删除子串可以从string的结尾处开始删除,格式如下:

${str%substr}    # 删除str结尾处与substr匹配的最短子串
${str%%substr}    # 删除str结尾处与substr匹配的最长子串

  结尾处删除子串:

0

2.6、替换子串

  替换子串命令是${...}格式,可以在任意处(开头处和结尾处)替换满足条件的子串。替换任意子串的命令,命令格式如下:

${str/substr/replacement}    # 仅替换第一次与substr相匹配的子串
${str//substr/replacement}  # 替换所有与substr相匹配的子串 

  详情如下:

0

  替换子串命令还有两种格式,分别在string开头和结尾处替换与substr相匹配的子串,格式为:

${str/#substr/replacement}    # 替换str开头处与substr相匹配的子串
${str/%substr/replacement}  # 替换str结尾处与substr相匹配的子串 

  详情如下:

0

  必须替换开头和结尾的字符串。

3、有类型变量

  Shell变量一般是无类型的,但bash Shell提供了declare和typeset两个命令用于执行变量的类型,两个命令是完全等价的。

  declare命令的格式:

declare [选项] 变量名

  declare命令有6个选项,详情如下:

选项名

含义

-r

将变量设置为只读属性

-i

将变量定义为整型数

-a

将变量定义为数组

-f

显示此脚本前定义过的所有函数名及其内容

-F

仅显示此脚本前定义过的所有函数名

-x

将变量声明为环境变量

  declare命令的-r选项将变量设置为只读属性,设置为只读后,变量值不允许再被修改。

  declare命令的-i选项将变量定义为整数型,一旦变量定义为整型数,Shell不再按照字符串形式来处理该变量。允许使用该变量进行算术运算。

#!/bin/bash

# 字符型处理 var2
var=2009
var2=$var+1
echo "var2=$var2"

# let命令以整型处理var3
let var3=$var+1
echo "var3=$var3"

# 将var4定义为整型
declare -i var4
var4=$var+1
echo "var4=$var4"

  执行结果如下:

0

  双圆括号方法是一种使变量执行蒜素运算的方法,即((...))格式。

#!/bin/bash

var=12
var2=5

# 用双圆括号括起来使变量进行算术运算
result=$((var*var2))
echo "result=$result"

  执行结果如下:

0

4、间接变量的引用

  若第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值就成为间接变量引用。

  设有如下两个表达式:

var=var2
var2=value

  var的值是var2,而var2是变量名,var2的值为value,间接变量引用是指通过var获得变量值value的行为,bash Shell提供了两种格式实现间接变量引用。

eval tempvar=\$$var1
tempvar=${!var1}

  第1种格式中eval是关键字,用\$$形式得到var1的间接引用,保存在tempvar变量中;

  第2种格式用${!...}得到var1的间接引用,并赋给tempvar。

  新建 indirect.sh 脚本,详情如下:

#!/bin/bash

var=var2    # 定义var变量,值为var2
var2=Hadoop    # 定义var2变量,值为Hadoop

echo "var=$var"

eval tempvar=\$$var
echo "tempvar=$tempvar"

echo "Indiret ref var is: ${!var}"

  定义var变量,值为var2,而var2也是一个变量名,值为Hadoop。执行结果如下:

0

  直接引用var的结果是变量名var2,间接引用var的结果为Hadoop。

  间接变量引用示例:

#!/bin/bash

##### 数据库表格数据 #####
s1_name="Zhang San"
s1_dept=Computer
s1_phone=431231231
s1_rank=5

s2_name="Li Xiang"
s2_dept=English
s2_phone=8346524
s2_rank=8

s3_name="Zhou Jin"
s3_dept=Physic
s3_phone=83680010
s3_rank=3
##### 数据库表格数据 #####

PS3='Pls. select the number of student:'

# 用select建立菜单,供用户选择学号
select stunum in "s1" "s2" "s3"
do
    # 用输入的学号组合成名字、系名、电话和排名的变量名
    name=${stunum}_name
    dept=${stunum}_dept
    phone=${stunum}_phone
    rank=${stunum}_rank
    
    # 通过间接变量引用得到学生的信息
    echo "BASIC INFOMATION OF NO.$stunum STUDENT:"
    echo "NAME: ${!name}"
    echo "DEPARTMENT: ${!dept}"
    echo "PHONE: ${!phone}"
    echo "RANK: ${!rank}"
        
        break
done

  赋予执行权限,执行结果如下:

 0