变量

一 变量介绍

什么是变量?

量指的是记录事物的状态

变指的是事物的状态是可以发生变化的

变量本质就是一种数据存取的机制,变量的数据都是存放于内存中的


为何要有变量?

程序运行的本质就是一系列状态的变化,

程序=数据+功能,(程序=数据架构+算法)
程序执行的本质就是一系列状态的变化,变是程序执行的直接体现,所以我们需要有一种机制能够反映或者说是保存下来程序执行时状态以及状态的变化。
# 比如:
    英雄的等级为1,打怪升级(变)为10
    僵尸的存活状态True,被植物打死了,于是变为False
    人的名字为egon,也可以修改为Egon

二 变量的使用

2.1 先定义、后引用

先定义

# 1、语法:变量名=值
# 2、注意:等号左右两边不能有空格!!!
# 3、例如:
[root@localhost shell]# name="egon"

后引用

[root@localhost shell]# ip="192.168.11.10"
[root@localhost shell]# echo $ip
192.168.11.10

注意:如果是打印百分比,建议使用${变量名}%
[root@localhost shell]# percent=33
[root@localhost shell]# echo ${percent}%
33%

2.2 删除变量

[root@localhost shell]# x=111
[root@localhost shell]# unset x
[root@localhost shell]# echo $x

[root@localhost shell]#

三 变量的三大组成部分

基于上一小节我们得知,定义一个变量由三大部分组成

  • 1、变量名:用来访问到变量值的
  • 2、赋值符号:将变量值的内存地址绑定给变量名
  • 3、变量值:即我们存的数据

3.1 变量名的命名规范

定义变量把数据存到内存中不是目的,目的是以后要取出来用,而变量名就是用来取变量值的,而变量的名字直接表明了所存值的意义,可见变量名的命名非常重要

# 变量名的命令应该见名知意,同时遵循如下规则
以字母或下划线开头,剩下的部分可以是:字母、数字、下划线,最好遵循下述规范:
    1.以字母开头
    2.使用中划线或者下划线做单词的连接
    3.同类型的用数字区分
    4.对于文件名的命名最好在末尾加上拓展名
    
例如: sql_bak.tar.gz,log_bak.tar.bz2  
    5、不要带有空格、?、*等特殊字符
    6、不能使用bash中的关键字,例如if,for,while,do等
    7、不要和系统环境变量冲突

3.2 变量值的三种来源

(1)直接赋值

# 1. 显式赋值:变量名=变量值
示例:
ip1=192.168.11.200
school="Shanghai oldboy"
today1=`date +%F`
today2=$(date +%F)

# 2、应用示例
[root@localhost ~]# url="www.baidu.com"
[root@localhost ~]# echo $url
www.baidu.com
[root@localhost ~]# url="www.sina.com.cn"
[root@localhost ~]# echo $url
www.sina.com.cn
补充1、
引号
在Shell中,引号主要用来转换元字符的含义。所谓元字符是指那些在正则表达式中具有特殊处理能力的字符,例如$、\、>等字符。

Shell中的引号有三种:单引号('')、双引号("")与反引号(``)。接下来分别来学习一下这几种引号。

1、单引号
单引号可以将它中间的字符还原为字面意义,实现屏蔽Shell元字符的功能。引号里的字符串就是一个单纯的字符串,
没有任何含义,例如定义变量NUM=100,在输出变量时需要添加$符号,如果这个变量加上单引号输出,则直接将$符
号与变量整体作为一个字符串输出,命令如下所示。

[itheima@localhost ~]$NUM=100
[itheima@localhost ~]$echo $NUM
100
[itheima@localhost ~]$echo '$NUM'
$NUM
在第二次加单引号输出$NUM时,直接输出了一个字符串而不是值100,单引号将$符号的功能屏蔽了。

注意:不可以在两个单引号中间单独插入一个单引号,单引号必须要成对出现。

2、双引号
双引号也具有屏蔽作用,但它不会屏蔽$符号、\符号和``符号。将刚才定义的变量NUM加双引号输出,具体如下所示:

[itheima@localhost ~]$echo "$NUM"
100
由以上输出结果可知,使用双引号输出变量NUM时,$符号的功能不会被屏蔽。

注意:双引号也可以屏蔽单引号的作用,在一对双引号中,单引号不必成对出现。

3、反引号
反引号,它可以进行命令替换。反引号与双引号可以结合使用。例如输出系统的时间,具体操作如下。

[itheima@localhost ~]$echo "Today is `date`"
Today is 2017年09月07日星期四10:50:26 CST
以上所示的命令中用到了命令“date”, 该命令的功能是打印系统当前的时间 。

可以把反引号嵌入到双引号中,但是当把反引号嵌入到单引号中时,单引号会屏蔽掉反引号的功能。例如把`date`嵌入单引号中,将不会打印出当前的时间。

[itheima@localhost ~]$echo 'Today is `date`'
Today is `date`
通配符
Shell的通配符一般用于数据处理或文件名匹配,常用的通配符如下所示。

1、通配符“*”
如果用户想要列出/etc目录下以sys开头的所有文件,可以使用如下命令。

    [itheima@localhost~]$ls -d /etc/sys*
  sysconfig       sysctl.conf       sysctl.d       systemd      system-release 
system-release-cpe
在以上命令中,sys*表示匹配以字符串sys开头的所有文件。-d选项表示仅对目标目录本身进行处理,不递归处理目录中的文件。

如果想输出以.conf结尾的所有文件,则可以使用如下命令。

[itheima@localhost ~]$ls /etc/*.conf
asound.conf          fuse.conf     man_db.conf     rsyncd.conf
autofs.conf          GeoIP.conf    mke2fs.conf     rsyslog.conf
autofs_ldap_auth.conf      host.conf     mtools.conf     sestatus.conf
brltty.conf              idmapd.conf    nfsmount.conf sos.conf
......
在这个命令中,*.conf表示匹配所有以.conf字符串结尾的文件,此命令会输出所有以.conf结尾的文件。因为文件太多,在这里只截取一部分。

2、通配符“?”
通配符“?”每次只能匹配一个字符,通常与其它通配符结合使用。如果想查找/etc目录下文件名是由两个字符组成的文件,可以使用如下命令。

[itheima@localhost ~]$ls -d /etc/??
hp pm
3、通配符“[]”
通配符“[]”表示与[]中的任一字符匹配,它通常是一个范围,例如,在/etc目录,列出以f~h范围的字母开头,并以.conf结尾的文件,可以使用如下命令。

[itheima@localhost ~]$ls /etc/[f-h]*.conf
fprintd.conf fuse.conf GeoIP.conf host.conf
由输出结果可知,/etc目录下以f~h范围内的字母开头,并以.conf结尾的文件有四个。

4、通配符“[!]”
通配符“[!]”表示除了[]里的字符,与其它任一字符匹配。例如,如果查找以y开头且不以.conf结尾的文件,可以使用如下命令。

[itheima@localhost ~]$ls -d /etc/y*[!.conf]
yum yum.repos.d
由输出结果可知,/etc目录下符号条件的匹配项有两个。

连接符
Shell中提供了一组用于连接命令的符号,包括:“;”、“&&”以及“||”, 使 用 这 些 符 号 ,可以将多条Shell指令进行连接,使这些指令顺序或根据命令执行结果,有选择地执行。下面将对这些符号的功能分别进行介绍。

1、“;”连接符
使用“;”连接符间隔的命令,会按照先后次序依次执行。假如现在有一系列确定的操作需要执行,且这一系列操作的执行需要耗费一定时间,比如安装gdb包时,在下载好安装包后,还需要逐个执行以下命令:

[root@localhost ~]#tar –xzvf gdb-7.11.1.tar.gz
[root@localhost ~]#cd gdb-7.11.1
[root@localhost ~]#./configure
[root@localhost ~]#make
[root@localhost ~]#make install
[root@localhost ~]#gdb -v
且在大多数命令开始执行后,都需要一定的时间,等待命令执行完毕。若此时使用“;”连接符,连接这些命令,具体如下所示:

[root@localhost ~]#tar -xzvf gdb-7.11.1.tar.gz ;cd gdb-7.11.1;./configure;
make;makeinstall;gdb -v
那么系统会自动执行这一系列命令。

2、“&&”连接符
使用“&&”连接符连接的命令,其前后命令的执行遵循逻辑与关系,只有该连接符之前的命令执行成功后,它后面的命令才被执行。

3、“||”连接符
使用“||”连接符连接的命令,其前后命令的执行遵循逻辑或关系,只有该连接符之前的命令执行失败时,它后面的命令才会执行。
补充2、
1. ()用法
1.1 命令替换 $()
为什么叫命令替换,不直接叫表达式赋值语法呢?

注意:括号前有个$符号,并且括号的内容必须是shell命令,可以携带参数,即可以直接在linux 的cmd命令行执行

命令就是将shell命令的输出赋给变量,它是脚本编程中的一个主要组成部分。

 $ date
2022年 02月 28日 星期一 15:34:24 CST
 $ test=$(date)
 $ echo $test
2022年 02月 28日 星期一 15:34:34 CST
 $

除了使用$() 进行命令替换以外,还可以用 `` 符号。 注意在美式键盘上,它通常和波浪线( ~)位于同一键位。

$ test2=`date`
 $ echo $test2
2022年 02月 28日 星期一 15:36:50 CST

1.2 命令分组
我们知道可以 把多条命令放在一起执行,并通过;分号进行分割命令,这个方式叫做命令列表

命令分组就是在多命令基础上,额外多了括号,例如 (cmd1;cmd2;cmd3)

注意 命令分组

括号中的命令将会新开一个子shell顺序执行,所以括号中的变量不能够被脚本余下的部分使用。括号中多个命令之间用分号隔开,最后一个命令可以没有分号,各命令和括号之间不必有空格。

 $ (pwd ;ls ;echo  $BASH_SUBSHELL )
/Data2/Team/huangtianbao/learn/source/test
123  tests.c
1
 $ pwd ;ls ;echo  $BASH_SUBSHELL
/Data2/Team/huangtianbao/learn/source/test
123  tests.c
0

1.3 用于初始化数组
在shell 中也是支持定义数组的,数组的赋值是通过()括起来的,数组元素之间通过空格分隔,并且数组的下标是从零开始的。其中和其他编译语言一样要访问数组元素需要通过数组名+[下标]的方式访问。[*] 比较特别,它可以列出数组的所有元素。并且变量名指代的是数组变量的第一个元素也就是[0]。

 $ test=(a b c d)
 $ echo ${test[*]}
 a b c d
 $ echo ${test[0]}
 a
 $ echo $test
 a

2 (())用法
2.1 对整数进行数学运算
双小括号 (( )) 是 Bash Shell 中专门用来进行整数运算的命令,它的效率很高,写法灵活,是企业运维中常用的运算命令。

注意:(( )) 只能进行整数运算,不能对小数(浮点数)或者字符串进行运算。后续讲到的 bc 命令可以用于小数运算。

双括号命令的格式如下:

(( expression ))
通俗地讲,就是将数学运算表达式放在(())之间。

表达式可以只有一个,也可以有多个,多个表达式之间以逗号,分隔。对于多个表达式的情况,以最后一个表达式的值作为整个 (( )) 命令的执行结果。

可以使用 $ 获取 (( )) 命令的结果,这和使用$获得变量值是类似的。

表1:(( )) 的用法
运算操作符/运算命令	说明
((a=10+66)
((b=a-15))
((c=a+b))	这种写法可以在计算完成后给变量赋值。以 ((b=a-15)) 为例,即将 a-15 的运算结果赋值给变量 c。

注意,使用变量时不用加$前缀,(( )) 会自动解析变量名。
a=$((10+66)
b=$((a-15))
c=$((a+b))	可以在 (( )) 前面加上$符号获取 (( )) 命令的执行结果,也即获取整个表达式的值。以 c=$((a+b)) 为例,即将 a+b 这个表达式的运算结果赋值给变量 c。

注意,类似 c=((a+b)) 这样的写法是错误的,不加$就不能取得表达式的结果。
((a>7 && b==c))	(( )) 也可以进行逻辑运算,在 `if` 语句中常会使用逻辑运算。
echo $((a+10))	需要立即输出表达式的运算结果时,可以在 (( )) 前面加$符号。
((a=3+5, b=a+10))	对多个表达式同时进行计算。
在 (( )) 中使用变量无需加上$前缀,(( )) 会自动解析变量名,这使得代码更加简洁,也符合程序员的书写习惯。

除了支持简单的加减乘除外,还支持:
在这里插入图片描述
实例演示:

【实例1】利用 (( )) 进行简单的数值计算。

[c.biancheng.net]$ echo $((1+1))
2
[c.biancheng.net]$ echo $((6-3))
3
[c.biancheng.net]$ i=5
[c.biancheng.net]$ ((i=i*2))  #可以简写为 ((i*=2))。
[c.biancheng.net]$ echo $i   #使用 echo 输出变量结果时要加 $。
10
【实例2】用 (( )) 进行稍微复杂一些的综合算术运算。

[c.biancheng.net]$ ((a=1+2**3-4%3))
[c.biancheng.net]$ echo $a
8
[c.biancheng.net]$ b=$((1+2**3-4%3)) #运算后将结果赋值给变量,变量放在了括号的外面。
[c.biancheng.net]$ echo $b
8
[c.biancheng.net]$ echo $((1+2**3-4%3)) #也可以直接将表达式的结果输出,注意不要丢掉 $ 符号。
8
[c.biancheng.net]$ a=$((100*(100+1)/2)) #利用公式计算1+2+3+...+100的和。
[c.biancheng.net]$ echo $a
5050
[c.biancheng.net]$ echo $((100*(100+1)/2)) #也可以直接输出表达式的结果。
5050
【实例3】利用 (( )) 进行逻辑运算。

[c.biancheng.net]$ echo $((3<8))  #3<8 的结果是成立的,因此,输出了 1,1 表示真
1
[c.biancheng.net]$ echo $((8<3))  #8<3 的结果是不成立的,因此,输出了 0,0 表示假。
0
[c.biancheng.net]$ echo $((8==8)) #判断是否相等。
1
[c.biancheng.net]$ if ((8>7&&5==5))
> then
> echo yes
> fi
yes
最后是一个简单的 if 语句的格式,它的意思是,如果 8>7 成立,并且 5==5 成立,那么输出 yes。显然,这两个条件都是成立的,所以输出了 yes。

【实例4】利用 (( )) 进行自增(++)和自减(–)运算。

[c.biancheng.net]$ a=10
[c.biancheng.net]$ echo $((a++))  #如果++在a的后面,那么在输出整个表达式时,会输出a的值,因为a为10,所以表达式的值为10。
10
[c.biancheng.net]$ echo $a #执行上面的表达式后,因为有a++,因此a会自增1,因此输出a的值为11。
11
[c.biancheng.net]$ a=11
[c.biancheng.net]$ echo $((a--)) #如果--在a的后面,那么在输出整个表达式时,会输出a的值,因为a为11,所以表达式的值的为11。
11
[c.biancheng.net]$ echo $a #执行上面的表达式后,因为有a--,因此a会自动减1,因此a为10。
10
[c.biancheng.net]$ a=10
[c.biancheng.net]$ echo $((--a))  #如果--在a的前面,那么在输出整个表达式时,先进行自增或自减计算,因为a为10,且要自减,所以表达式的值为9。
9
[c.biancheng.net]$ echo $a #执行上面的表达式后,a自减1,因此a为9。
9
[c.biancheng.net]$ echo $((++a))  #如果++在a的前面,输出整个表达式时,先进行自增或自减计算,因为a为9,且要自增1,所以输出10。
10
[c.biancheng.net]$ echo $a  #执行上面的表达式后,a自增1,因此a为10。
10
本教程假设读者具备基本的编程能力,相信读者对于前自增(前自减)和后自增(后自减)的区别也非常清楚,这里就不再赘述,只进行简单的说明:
执行 echo $((a++)) 和 echo $((a–)) 命令输出整个表达式时,输出的值即为 a 的值,表达式执行完毕后,会再对 a 进行 ++、-- 的运算;
而执行 echo $((++a)) 和 echo $((–a)) 命令输出整个表达式时,会先对 a 进行 ++、-- 的运算,然后再输出表达式的值,即为 a 运算后的值。

【实例5】利用 (( )) 同时对多个表达式进行计算

[c.biancheng.net]$ ((a=3+5, b=a+10))  #先计算第一个表达式,再计算第二个表达式
[c.biancheng.net]$ echo $a $b
8 18
[c.biancheng.net]$ c=$((4+8, a+b))  #以最后一个表达式的结果作为整个(())命令的执行结果
[c.biancheng.net]$ echo $c
26
2.2 逻辑运算
这里单独把逻辑运算列出来,是为了强调 (()) ,成立 输出1,不成立输出0,这个和 []、[[]]是反过来的

【实例3】利用 (( )) 进行逻辑运算。

[c.biancheng.net]$ echo $((3<8))  #3<8 的结果是成立的,因此,输出了 1,1 表示真
1
[c.biancheng.net]$ echo $((8<3))  #8<3 的结果是不成立的,因此,输出了 0,0 表示假。
0
[c.biancheng.net]$ echo $((8==8)) #判断是否相等。
1
[c.biancheng.net]$ if ((8>7&&5==5))
> then
> echo yes
> fi
yes
3 中括号、方括号[]
3.1 字符串和数值比较 []
if   [ condition ]
then
commands
fi

等价于

if  test  condition 
then
commands
fi

test命令可以判断三类条件:
数值比较
字符串比较
文件比较

3.2 用于正则表达式
字符范围。用作正则表达式的一部分,描述一个匹配的字符范围。作为if [] 用途的中括号内不能使用正则。

示例:
使用了中括号以及在特定位置上可能出现的两种字符: 1或3。中括号表示一个字符位置并给出多个可能的选择。可以像上面的例子那样将待选的字符列出来,也可以指定字符范围,例如字母范围[1–3]。

$ ls
123  223  323  tests.c
$ ls [13]*
123  323
$ ls [1-3]*
123  223  323

3.3 数学运算$[]
一般数学运算可以用,expr 命令但是expr 命令会有缺点,它在处理一些特定字符会出现问题,例如 “ * ”星号符 。在遇到特俗符号时需要使用shell的转义字符(反斜线)将其标出来。

$ expr 1 + 5
6
$ expr 5 * 2
expr: syntax error
$ expr 5 \* 2
10

用方括号执行shell数学运算比用expr命令方便很多。在使用方括号来计算公式时,不用担心shell会误解乘号或其他符号。 shell知道它不是通配符,因为它在方括号内。bash shell数学运算符只支持整数运算。若要进行任何实际的数学计算,这是一个巨大的限制。

$ var1=$[1 + 5]
$ echo $var1
6
$ var2=$[$var1 * 2]
$ echo $var2
12
 $ var1=100
 $ var2=45
 $ var3=$[$var1 / $var2]
 $ echo The final result is $var3
 The final result is 2

4. { }用法
4.1 变量范围限定${}
根据下面的shell 命令可见 如果变量 a 没有用{} 括起来的话,shell 就会将abc 识别为变量名。这显然不是我们想要的结果

 $ a=123456
 $ echo $abc

 $ echo ${a}bc
123456bc
 $

4.2 命令分组 { ;;}
注意 “{” 后面需要有空格,“}” 前可以不需要空格 但是最后一条命令需要结束后也需要加“;” 。可以看到echo $BASH_SUBSHELL 输出的为 0,说明命令分组 { ;;} 并没有创建子shell 执行命令。

$ { ls ; ls ;echo  $BASH_SUBSHELL; }
123  223  323  tests.c
123  223  323  tests.c
0

4.3 用于通配符
将对大括号中的文件名做扩展。在大括号中,不允许有空白,除非这个空白被引用或转义。第一种:对大括号中的以逗号分割的文件列表进行拓展。如 touch {a,b}.txt 结果为a.txt b.txt。第二种:对大括号中以点点(…)分割的顺序文件列表起拓展作用,如:touch {a…d}.txt 结果为a.txt b.txt c.txt d.txt

# ls {ex1,ex2}.sh 
ex1.sh ex2.sh 
# ls {ex{1..3},ex4}.sh 
ex1.sh ex2.sh ex3.sh ex4.sh 
# ls {ex[1-3],ex4}.sh 
ex1.sh ex2.sh ex3.sh ex4.sh
4.4 几种特殊的替换结构
${var:-string},${var:+string},${var:=string},${var:?string}
① v a r : − s t r i n g 和 {var:-string}和 var:−string和{var:=string}:若变量var为空,则用在命令行中用string来替换 v a r : − s t r i n g ,否则变量 v a r 不为空时,则用变量 v a r 的值来替换 {var:-string},否则变量var不为空时,则用变量var的值来替换 var:−string,否则变量var不为空时,则用变量var的值来替换{var:-string};对于 v a r : = s t r i n g 的替换规则和 {var:=string}的替换规则和 var:=string的替换规则和{var:-string}是一样的,所不同之处是 v a r : = s t r i n g 若 v a r 为空时,用 s t r i n g 替换 {var:=string}若var为空时,用string替换 var:=string若var为空时,用string替换{var:=string}的同时,把string赋给变量var:${var:=string}很常用的一种用法是,判断某个变量是否赋值,没有的话则给它赋上一个默认值。

② ${var:+string}的替换规则和上面的相反,即只有当var不是空的时候才替换成string,若var为空时则不替换或者说是替换成变量 var的值,即空值。(因为变量var此时为空,所以这两种说法是等价的)

③ v a r : ? s t r i n g 替换规则为:若变量 v a r 不为空,则用变量 v a r 的值来替换 {var:?string}替换规则为:若变量var不为空,则用变量var的值来替换 var:?string替换规则为:若变量var不为空,则用变量var的值来替换{var:?string};若变量var为空,则把string输出到标准错误中,并从脚本中退出。我们可利用此特性来检查是否设置了变量的值。

补充扩展:在上面这五种替换结构中string不一定是常值的,可用另外一个变量的值或是一种命令的输出。

4.5 四种模式匹配替换结构
模式匹配记忆方法:

#是去掉左边(在键盘上#在$之左边)

%是去掉右边(在键盘上%在$之右边)

#和%中的单一符号是最小匹配,两个相同符号是最大匹配。

${var%pattern},${var%%pattern},${var#pattern},${var##pattern}
第一种模式:${variable%pattern},这种模式时,shell在variable中查找,看它是否一给的模式pattern结尾,如果是,就从命令行把variable中的内容去掉右边最短的匹配模式

第二种模式:${variable%%pattern},这种模式时,shell在variable中查找,看它是否一给的模式pattern结尾,如果是,就从命令行把variable中的内容去掉右边最长的匹配模式

第三种模式:${variable#pattern} 这种模式时,shell在variable中查找,看它是否一给的模式pattern开始,如果是,就从命令行把variable中的内容去掉左边最短的匹配模式

第四种模式:${variable##pattern} 这种模式时,shell在variable中查找,看它是否一给的模式pattern结尾,如果是,就从命令行把variable中的内容去掉右边最长的匹配模式

这四种模式中都不会改变variable的值,其中,只有在pattern中使用了*匹配符号时,%和%%,#和##才有区别。结构中的pattern支持通配符,*表示零个或多个任意字符,?表示仅与一个任意字符匹配,[…]表示匹配中括号里面的字符,[!..]表示不匹配中括号里面的字符。

# var=testcase 
# echo $var 
testcase 
# echo ${var%s*e} 
testca 
# echo $var 
testcase 
# echo ${var%%s*e} 
te 
# echo ${var#?e} 
stcase 
# echo ${var##?e} 
stcase 
# echo ${var##*e}
# echo ${var##*s} 
e 
# echo ${var##test} 
case
4.6 字符串提取和替换
${var:num},${var:num1:num2},${var/pattern/pattern},${var//pattern/pattern}

第一种模式: v a r : n u m ,这种模式时, s h e l l 在 v a r 中提取第 n u m 个字符到末尾的所有字符。若 n u m 为正数,从左边 0 处开始;若 n u m 为负数,从右边开始提取字串,但必须使用在冒号后面加空格或一个数字或整个 n u m 加上括号,如 {var:num},这种模式时,shell在 var中提取第num个字符到末尾的所有字符。若num为正数,从左边0处开始;若num为负数,从右边开始提取字串,但必须使用在冒号后面加空格或一个数字或整个num加上括号,如 var:num,这种模式时,shell在var中提取第num个字符到末尾的所有字符。若num为正数,从左边0处开始;若num为负数,从右边开始提取字串,但必须使用在冒号后面加空格或一个数字或整个num加上括号,如{var: -2}、 v a r : 1 − 3 或 {var:1-3}或 var:1−3或{var:(-2)}。

第二种模式: v a r : n u m 1 : n u m 2 , n u m 1 是位置, n u m 2 是长度。表示从 {var:num1:num2},num1是位置,num2是长度。表示从 var:num1:num2,num1是位置,num2是长度。表示从var字符串的第 n u m 1 个位置开始提取长度为 num1个位置开始提取长度为 num1个位置开始提取长度为num2的子串。不能为负数。

第三种模式:${var/pattern/pattern}表示将var字符串的第一个匹配的pattern替换为另一个pattern。

第四种模式:${var//pattern/pattern}表示将var字符串中的所有能匹配的pattern替换为另一个pattern。

[root@centos ~]# var=/home/centos 
[root@centos ~]# echo $var 
/home/centos
[root@centos ~]# echo ${var:5} 
/centos
[root@centos ~]# echo ${var: -6} 
centos 
[root@centos ~]# echo ${var:(-6)} 
centos 
[root@centos ~]# echo ${var:1:4} 
home 
[root@centos ~]# echo ${var/o/h} 
/hhme/centos
[root@centos ~]# echo ${var//o/h} 
/hhme/cenths

(2)从位置参数获取变量值

从调用脚本时传入的位置参数获取变量值:./b.sh a1 a2 a3
需要用到$n获取第n个位置参数值,超过10需要用${n},如下
$0 $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10}
(当脚本需要的命令行参数不止9个时,必须在变量数字周围加
上花括号,比如${10}表示第十个参数。)

# 示例
[root@egon ~]# cat b.sh 
#!/usr/bin/env bash
echo ${0}
echo $1
echo $2
echo $3
echo $4
echo $5
echo $6
echo $7
echo $8
echo $9
echo ${10}
echo ${11}
echo ${12}

# 运行
[root@egon ~]# chmod +x b.sh 
[root@egon ~]# ./b.sh a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15
./b.sh
a1
a2
a3
a4
a5
a6
a7
a8
a9
a10
a11
a12

# 企业使用:可以实现脚本的不同功能(服务程序管理脚本-启动功能 关闭功能 重启功能)
server.sh  start|stop|restart

(3)与用户交互获取值

何为交互,即输入、输出

# 一:read接收用户的输入,即从键盘读入变量值
read 变量名
read -p "提示信息: "  变量名
read -t 5 -p "提示信息: "  变量名  # -t指定秒数
read -n 2 变量名  # -n读取的字符个数

=======》应用示例:vim first.sh
back_dir1=/var/backup
read -p "请输入你的备份目录: " back_dir2
echo $back_dir1
echo $back_dir2

企业使用:可以根据需求调整变量值,可以便于新员工快速掌握企业脚本使用

# 二:输出
# 2.1 echo命令,详解情况:https://www.cnblogs.com/linhaifeng/articles/13976349.html
[root@egon ~~]# name="egon"
[root@egon ~~]# age=18
[root@egon ~~]# echo -e "my name is $name my age is $age"
my name is egon my age is 18

还可以输出带颜色(了解即可)
echo -e "\033[31m 红色字 \033[0m"
echo -e "\033[34m 黄色字 \033[0m"
echo -e "\033[41;33m 红底黄字 \033[0m"
echo -e "\033[41;37m 红底白字 \033[0m"
 
# 2.2 prinf命令(C语言风格)
%s
%d
%7.3f  打印浮点数,总宽度为7,小数位保留3位,并且四舍五入
[root@egon ~~]# name="egon"
[root@egon ~~]# age=18
[root@egon ~~]# printf "my name is %s my age is %s\n" $name $age
my name is egon my age is 18        


[root@egon ~]# salary=3.3339
[root@egon ~]# printf "my name is %s my age is %7.3f\n" $name $salary
my name is egon my age is   3.334

三 预定变量

$*  所有的参数
$@  所有的参数
$#  参数的个数
$$  当前进程的PID  # 此外,可以使用只读变量来获取父进程的PID:$PPID、获取执行脚本的用户ID:$UID
$?  上一个命令的返回值 0表示成功 


示例1:
[root@egon ~]# chmod +x b.sh 
[root@egon ~]# ./b.sh a1 a2 a3 a4 a5
a1 a2 a3 a4 a5
a1 a2 a3 a4 a5
5
18988
1
[root@egon ~]# cat b.sh 
#!/usr/bin/env bash
echo $*
echo $@
echo $#
echo $$

ping -c1 192.168.11.10 &>/dev/null
echo $?

示例2:
[root@egon ~]# vim ping.sh
#!/bin/bash                         
ping -c2 $1 &>/dev/null     
if [ $? = 0 ];then                  
    echo "host $1 is ok"        
else                                            
    echo "host $1 is fail"  
fi

[root@egon ~]# chmod +x ping.sh
[root@egon ~]# ./ping.sh 192.168.11.10

了解:

如果我们想从命令行中获取脚本调用者传入的参数值,用$n可以取到,但如果脚本调用者在命令行传入的参数个数不固定,那么此时就需要用$*或$@来获取了

$*与$@获取的是所有位置参数,$0除外

当脚本调用者的传参形式如下是

[root@localhost ~]# ./script.sh 命令1 命令2 命令3

针对for循环语句:for i in 元素,for循环会按照空格作为分隔符来一个个取元素,所以此时$*$@并无区别

[root@localhost ~]# cat script.sh 
#!/bin/bash

for i in $*
do
    echo $i
done

echo "=================="

for i in $@
do
    echo $i
done
[root@localhost ~]# 
[root@localhost ~]# ./script.sh 命令1 命令2 命令3
命令1
命令2
命令3
==================
命令1
命令2
命令3

当脚本调用者的传参形式如下时

[root@localhost ~]# ./script.sh 命令1 命令2 "命令3 参数"

针对for循环语句:for i in 元素,for循环会按照空格作为分隔符来一个个取元素,所以此时$*$@如果不加引号,第三个命令:"命令3 参数",为被以空格为分隔符识别成两部分

[root@localhost ~]# cat script.sh 
#!/bin/bash

for i in $*
do
    echo $i
done

echo "=================="

for i in $@
do
    echo $i
done
[root@localhost ~]# 
[root@localhost ~]# ./script.sh 命令1 命令2 "命令3 参数"
命令1
命令2
命令3
参数
==================
命令1
命令2
命令3
参数

所以需要为$*$@加上引号

[root@localhost ~]# cat script.sh 
#!/bin/bash

for i in "$*"
do
    echo $i
done

echo "=================="

for i in "$@"
do
    echo $i
done
[root@localhost ~]# ./script.sh 命令1 命令2 "命令3 参数"
命令1 命令2 命令3 参数
==================
命令1
命令2
命令3 参数
[root@localhost ~]#

但此时为$*就会把所有位置参数识别成一个整体,所以总结如下:

当脚本调用者的传参形式如下时
[root@localhost ~]# ./script.sh 命令1 命令2 "命令3 参数"

需要使用"$@"来分别获取一个个完整的命令
for i in "$@"
do
    echo $i
done

其余情况$*与$@完全一致

四 常量

相对于变量,常量就是不可以被改变的量,即只能读不能改,所以又称之为只读变量

使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。

[root@egon ~]# x=111
[root@egon ~]# readonly x
[root@egon ~]# x=666
-bash: x: 只读变量