02-Shell 编程之变量

1、Shell的变量

变量可以分为两类:环境变量(全局变量)和普通变量(局部变量)

  • 环境变量也可称为全局变量,可以在创建他们的Shell及其派生出来的任意子进程shell中使用,环境变量又可分为自定义环境变量和Bash内置的环境变量

  • 普通变量也可称为局部变量,只能在创建他们的Shell函数或Shell脚本中使用。普通变量一般是由开发者用户开发脚本程序时创建的。

1.1、环境变量

使用 env|declare|set|export -p 等命令可以查看系统中的环境变量。

1
2
3
4
5
6
7
[lepeng@centos ~]# env
XDG_SESSION_ID=1
HOSTNAME=clsn
TERM=linux
SHELL=/bin/bash
HISTSIZE=1000
...

输出一个系统中的 环境变量

1
2
[lepeng@centos ~]# echo $LANG
zh_CN.UTF-8

1.1.1、环境变量相关配置文件

1
2
3
4
5
/etc/proflie
/etc/bashrc
~/.bashrc
~/.bash_profile
/etc/proflie.d/ # 目录

文件读取顺序(CentOS6和7都一样)

1
2
3
4
/etc/profile
~/.bash_profile
~/.bashrc
/etc/bashrc

文件读取过程示意图

验证文件读取顺序的方法

1
2
3
4
5
6
7
8
sed -i '1a echo "$(date +%T-%s) /etc/profile1" >>/tmp/clsn' /etc/profile
sed -i '$a echo "$(date +%T-%s) /etc/profile2" >>/tmp/clsn' /etc/profile
sed -i '1a echo "$(date +%T-%s) /etc/bashrc1" >>/tmp/clsn' /etc/bashrc
sed -i '$a echo "$(date +%T-%s) /etc/bashrc2" >>/tmp/clsn' /etc/bashrc
sed -i '1a echo "$(date +%T-%s) ~/.bashrc1" >>/tmp/clsn' ~/.bashrc
sed -i '$a echo "$(date +%T-%s) ~/.bashrc2" >>/tmp/clsn' ~/.bashrc
sed -i '1a echo "$(date +%T-%s) ~/.bash_profile1" >>/tmp/clsn' ~/.bash_profile
sed -i '$a echo "$(date +%T-%s) ~/.bash_profile2" >>/tmp/clsn' ~/.bash_profile

1.1.2、环境变量的一些注意点

  • 变量名通常要大写。
  • 变量可以在自身的Shell及子Shell中使用。
  • 常用export来定义环境变量。
  • env 默认可以显示所有的环境变量名称及对应的值。
  • 输出时用“$变量名”,取消时用“unset变量名”。
  • 书写cron定时任务时要注意,脚本要用到的环境变量最好先在所执行的Shell脚本中重新定义。
  • 如果希望环境变量永久生效,则可以将其放在用户环境变量文件或全局环境变量文件里。

1.2、普通变量

本地变量在用户当前的Shell生存期的脚本中使用。例如,本地变量 a = 1,这个值在当前 Shell 生存期中有意义。如果在 Shell 中启动另一个进程或退出,本地变量值将无效。例如:

1
2
3
4
5
6
7
8
9
10
# 提示:$变量名表示输出变量,可以用$c和${c}两种用法
[lepeng@centos ~]# a=1
[lepeng@centos ~]# b="2"
[lepeng@centos ~]# c="3"
[lepeng@centos ~]# echo "$a"
1
[lepeng@centos ~]# echo "$b"
2
[lepeng@centos ~]# echo "${c}"
3

小结:连续普通字符串内容赋值给变量,不管用什么引号或者不用引号,它的内容是什么,打印变量就输出什么

2 普通变量

2.0、变量名

  1. 变量名只能为字母、数字或下划线,只能以字母或下划线开头。
  2. 变量名的定义要有一定的规范,并且要见名知意。

示例:

1
2
3
LpAge=22      # 每个单词的首字母大写的写法
lp_age=22 # 单词之间用"_"的写法
lpSex=man # 驼峰语法:首个单词的首字母小写,其余单词首字母大写

2.1、变量中引号的使用

只有在变量的值中有空格的时候,会使用引号。单引号与双引号的区别在于,是否能够解析特殊符号。

  • 以单引号' '包围变量的值时,单引号里面是什么就输出什么,即使内容中有变量和命令(命令需要反引起来)也会把它们原样输出。
    这种方式比较适合定义显示纯字符串的情况,即不希望解析变量、命令等的场景。

  • 以双引号" "包围变量的值时,输出时会先解析里面的变量和命令,而不是把双引号中的变量名和命令原样输出。
    这种方式比较适合字符串中附带有变量和命令并且想将其解析后再输出的变量定义。

  • 反引号 ``是命令替换,命令替换是指 Shell 可以先执行``中的命令,将输出结果保存到变量。

建议:如果变量的内容是数字,那么可以不加引号;如果真的需要原样输出就加单引号;其他没有特别要求的字符串等最好都加上双引号,定义变量时加双引号是最常见的使用场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[lepeng@centos ~]# name=lp
[lepeng@centos ~]# name2='csdn'
[lepeng@centos ~]# name3="http://blog.lp"
[lepeng@centos ~]# echo $name # lp
[lepeng@centos ~]# echo $name2 # csdn
[lepeng@centos ~]# echo $name3 # http://blog.lp
[lepeng@centos ~]# name4='cs dn'
[lepeng@centos ~]# echo $name4 # cs dn
[lepeng@centos ~]# name5="cs dn"
[lepeng@centos ~]# echo $name5 # cs dn
[lepeng@centos ~]# name6='cs dn $PWD'
[lepeng@centos ~]# echo $name6 # cs dn $PWD
[lepeng@centos ~]# name6="cs dn $PWD"
[lepeng@centos ~]# echo $name6 # cl sn /root

2.1.1、变量中引号的使用场景

  1. 内容是纯数字、简单的连续字符(内容中不带任何空格)时,定义时可以不加任何引号,例如:

    1
    2
    LpAge=22
    NETWORKING=yes
  2. 没有特殊情况时,字符串一律用双引号定义赋值,特别是多个字符串中间有空格时,例如:

    1
    2
    NFSD_MODULE="no load"
    MyName="Oldboy is a handsome boy."
  3. 当变量里的内容需要原样输出时,要用单引号,这样的需求极少,例如:

    1
    aOLDBOY_NAME='OLDBOY'

变量使用反引号赋值

1
2
3
4
5
6
[lepeng@centos ~]# time=`date`
[lepeng@centos ~]# echo $time
2017年 12月 05日 星期二 09:02:06 CST
[lepeng@centos ~]# file=`ls`
[lepeng@centos ~]# echo $file
clsn_test.sh panduan.sh quanju.sh yhk.sh

2.2、定义变量的方式

三种定义变量的方式

  1. 直接赋值
  2. 传参(传递参数)
  3. 交互式设置变量,使用read命令

2.2.1、read命令说明

read命令的帮助说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Linux read 命令用于从标准输入读取数值。

read 内部命令被用来从标准输入读取单行数据。这个命令可以用来读取键盘输入,当使用重定向的时候,可以读取文件中的一行数据。

语法
read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]

参数说明:
-a 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符。
-d 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志。
-p 后面跟提示信息,即在输入前打印提示信息。
-e 在输入的时候可以使用命令补全功能。
-n 后跟一个数字,定义输入文本的长度,很实用。
-r 屏蔽\,如果没有该选项,则\作为一个转义字符,有的话 \就是个正常的字符了。
-s 安静模式,在输入字符时不再屏幕上显示,例如login时输入密码。
-t 后面跟秒数,定义输入字符的等待时间。
-u 后面跟fd,从文件描述符中读入,该文件描述符可以是exec新开启的。

在命令行中使用

1
2
3
4
5
6
7
8
9
10
[lepeng@centos ~]# read
132
[lepeng@centos ~]# echo $REPLY
132
[lepeng@centos ~]# read clsn
456
[lepeng@centos ~]# echo $clsn
456
[lepeng@centos ~]# echo $REPLY
132

在脚本中使用

1
2
3
4
5
6
7
8
9
[lepeng@centos ~]# vim test.sh 
#!/bin/bash
read -p '请输入:' a
echo $a

# 执行结果
[lepeng@centos ~]# sh test.sh
请输入:aaa
aaa

2.2.2、定义方法实践

直接赋值方法

1
2
3
4
5
6
7
8
9
10
[lepeng@centos ~]# vim bianliang.sh
#!/bin/bash
#
ethFile=/etc/sysconfig/network-scripts/ifcfg-eth0
echo $hobby
ls $ethFile

[lepeng@centos ~]# sh bianliang.sh
bianliang.sh chanshu.sh clsn.sh clsn_test.sh panduan.sh quanju.sh xiugaizhuji.sh yhk.sh
/etc/sysconfig/network-scripts/ifcfg-eth0

传参 (传递参数)

1
2
3
4
5
6
7
8
9
10
11
[lepeng@centos ~]# vim bianliang.sh
#!/bin/bash
#
hobby=$1
ethFile=$2
echo $hobby
ls $ethFile

[lepeng@centos ~]# sh bianliang.sh basketball /etc/hostname
basketball
/etc/hostname

交互式设置变量 read

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[lepeng@centos ~]# vim test.sh 
#!/bin/bash
#
read -p "请输入你的银行卡号:" Yhk
read -s -p "请输入密码:" miMa
echo
echo "你的银行卡号:" $Yhk
echo "你的密码为:" $miMa

[lepeng@centos ~]# sh test.sh
请输入你的银行卡号:123456
请输入密码:

你的银行卡号: 123456
你的密码为: 123456

2.2.3、写一个交互脚本,实现能够定义主机名及IP地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[lepeng@centos ~]#cat xiugaizhuji.sh 
#!/bin/bash
#
ethFile=/etc/sysconfig/network-scripts/ifcfg-eth[01]
Now_eth=`hostname -I|awk -F "[. ]+" '{print $4}'`

read -p "请输入主机名:" Hostname
read -p "请输入IP地址的主机位:" HostIP

hostnamectl set-hostname $Hostname

sed -i "s#${Now_eth}#${HostIP}#g" $ethFile

read -p "是否重启服务器:{yes/no}" REboot

if [ $REboot == yes ]
then
echo "系统将在10秒后重启!"
shutdown -r 10
else
echo "请稍后手动重启系统!"
fi

脚本测试结果

1
2
3
4
5
6
7
8
9
10
[lepeng@centos ~]# sh xiugaizhuji.sh
请输入主机名:clsn
请输入IP地址的主机位:180
是否重启服务器:{yes/no}yes
系统将在10秒后重启!
[lepeng@centos ~]# sh xiugaizhuji.sh
请输入主机名:clsn
请输入IP地址的主机位:180
是否重启服务器:{yes/no}no
请稍后手动重启!

2.3、变量引用 ${}

1
2
3
4
5
6
7
8
[lepeng@centos ~]# time=`date`
[lepeng@centos ~]# echo $time_day # 输出为空

[lepeng@centos ~]# echo ${time}_day
2017年 12月 05日 星期二 09:02:06 CST_day

[lepeng@centos ~]# echo $time-day
2017年 12月 05日 星期二 09:02:06 CST-day

3、特殊变量

3.1、位置变量

常用的特殊位置参数说明

位置变量 作用说明
$0 获取当前执行的shell脚本的文件名,如果执行脚本带路径那么就包括脚本路径。
$n 获取当前执行的shell脚本的第n个参数值,n=1..9,当n为0时表示脚本的文件名,如果n大于9用大括号括起来{10},参数以空格隔开。
$# 获取当前执行的shell脚本后面接的参数的总个数。
$* 获取当前shell的所有传参的参数,不加引号同 $@ ; 如果给 $* 加上双引号,例如:“$*”,则表示将所有的参数视为单个字符串,相当于 “112$3”
$@ 获取当前shell的所有传参的参数,不加引号同 $* ; 如果给 $@ 加上双引号,例如:“$@”,则表示将所有参数视为不同的独立字符串,相当于“$1” “$2” “$3”“……”,这是将参数传递给其他程序的最佳方式,因为他会保留所有内嵌在每个参数里的任何空白。当“$*”“$@”都加双引号时,两者有区别,都不加双引号时,两者无区别。

$#$* 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[lepeng@centos ~]# vim chanshu.sh
#!/bin/bash
#
echo $0
echo "第一个参数:" $1
echo "第二个参数:" $2
echo "第10个参数:" ${10}
echo "第11个参数:" ${11}
echo "参数个数:" $#
echo "参数:" $*

[lepeng@centos ~]# sh chanshu.sh 1 2 3 4 5 6 7 8 9 10 11 12
chanshu.sh
第一个参数: 1
第二个参数: 2
第10个参数: 10
第11个参数: 11
参数个数: 12
参数: 55 2 3 4 5 6 7 8 9 10 11 12

$*$@ 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[lepeng@centos ~]# set -- "I am" handsome boy..
[lepeng@centos ~]# echo $1
I am
[lepeng@centos ~]# echo $2
handsome
[lepeng@centos ~]# echo $3
boy..
[lepeng@centos ~]# echo $*
I am handsome boy..
[lepeng@centos ~]#echo $@
I am handsome boy..

[lepeng@centos ~]# for i in $*;do echo $i ;done
I
am
handsome
boy..
[lepeng@centos ~]# for i in $@;do echo $i ;done
I
am
handsome
boy..
[lepeng@centos ~]# for i in "$@";do echo $i ;done
I am
handsome
boy..
[lepeng@centos ~]# for i in "$*";do echo $i ;done
I am handsome boy..

3.2、进程状态变量

Shell进程的特殊状态变量说明

位置变量 作用说明
$? 获取执行上一个指令的执行状态返回值(0为成功,非零为失败),这个变量最常用。
$$ 获取当前执行的Shell脚本的进程号(PID),这个变量不常用,了解即可。
$! 获取上一个在后台工作的进程的进程号(PID),这个变量不常用,了解即可。
$_ 获取在此之前执行的命令或脚本的最后一个参数,这个变量不常用,了解即可。

示例

1
2
3
4
5
6
7
8
[lepeng@centos ~]# echo $?
0
[lepeng@centos ~]# echo $$
1368
[lepeng@centos ~]# echo $!

[lepeng@centos ~]# echo $_
echo

4、变量子串

4.1、变量子串说明

表达式 说明
${parameter} 返回变量$parameter的内容
${ #parameter} 返回变内容的长度(按字符),也适用于特殊变量
${parameter:offset} 在变量${parameter}中,从位置offset之后开始提取子串到结尾
${parameter:offset:length} 在变量${parameter}中,从位置offset之后开始提取长度为length的子串
${parameter#word} 从变量${parameter}开头开始删除最短匹配的word子串
${parameter##word} 从变量${parameter}开头开始删除最长匹配的word子串
${parameter%word} 从变量${parameter}结尾开始删除最短匹配的word子串
${parameter%%word} 从变量${parameter}结尾开始删除最长匹配的word子串
${parameter/pattem/string} 使用string代替第一个匹配的pattern
${parameter//pattem/string} 使用string代替所有匹配的pattern

计算变量的长度

1
2
3
4
5
[lepeng@centos ~]# a=http://blog.znix.top
[lepeng@centos ~]# echo ${a} |wc -L
20
[lepeng@centos ~]# echo ${#a}
20

截取变量中的字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[lepeng@centos ~]# test=abcABC123ABCabc
[lepeng@centos ~]# echo ${test#abc}
ABC123ABCabc
[lepeng@centos ~]# echo ${test##abc}
ABC123ABCabc
[lepeng@centos ~]# echo ${test%abc}
abcABC123ABC
[lepeng@centos ~]# echo ${test%%abc}
abcABC123ABC
[lepeng@centos ~]# echo ${test#a*c}
ABC123ABCabc
[lepeng@centos ~]# echo ${test##a*c}

[lepeng@centos ~]# echo ${test%a*c}
abcABC123ABC
[lepeng@centos ~]# echo ${test%%a*c}

[lepeng@centos ~]# echo ${test#a*C}
123ABCabc
[lepeng@centos ~]# echo ${test#a*C}
123ABCabc
[lepeng@centos ~]# echo ${test##a*C}
abc
[lepeng@centos ~]# echo ${test%a*c}
abcABC123ABC
[lepeng@centos ~]# echo ${test%A*c}
abcABC123
[lepeng@centos ~]# echo ${test%%A*c}
abc

替换变量内容

1
2
3
4
5
6
[lepeng@centos ~]# echo $test
abcABC123ABCabc
[lepeng@centos ~]# echo ${test/abc/test}
testABC123ABCabc
[lepeng@centos ~]# echo ${test//abc/test}
testABC123ABCtest

有关上述匹配删除的小结

  • # 表示从幵头删除匹配最短。
  • ## 表示从开头删除匹配最长。
  • % 表示从结尾删除匹配最短。
  • %% 表示从结尾删除匹配最长。
  • a*c 表示匹配的突符串,* 表示匹配所有,a*c 匹配开头为a、中间为任意多个字符、结尾为c的字符串。
  • a*C 表示匹配的字符串,* 表示匹配所有,a*C 匹配开头为a、中间为任意多个字符、结尾为C的字符串。

有关替换的小结

  • 一个“/”表示替换匹配的第-个字符串。
  • 两个“/”表示替换匹配的所有字符串。

4.2、Shell的特殊扩展变量说明

表达式 说明
${parameter:-word} 如果parameter的变量值为空或未赋值,则会返回word字符串并替代变量的值用途.如果变量未定义,则返回备用的值,防止变量为空值或因未定义而导致异常
${parameter:=word} 如果parameter的变量值为空或未赋值,则设置这个变量值为word,并返回其值。位置变量和特殊变量不适用用途:基本同上一个 ${parameter:-word},但该变量又额外给parameter变量赋值了
${parameter:?word} 如果parameter变量值为空或未赋值,那么word字符串将被作为标准错误输出,否则输出变量的值。用途:用于捕捉由于变量未定义而导致的错误,并退出程序
${parameter:+word} 如果parameter变量值为空或未赋值,则什么都不做,否则word字符串将替代变量的值

特殊变量实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[lepeng@centos ~]# cat test.sh 
#!/bin/bash
#
dir=
echo ${dir:-/tmp}
echo ${dir}
echo ${dir:=/mnt}
echo ${dir}
dir2= (空格)
echo ${dir2-/tmp}
echo ${dir2}
echo ${dir2:-/tmp}
echo ${dir2}
echo ${dir2=/mnt}
echo ${dir2}

测试结果

1
2
3
4
5
6
7
8
[lepeng@centos ~]# sh test.sh 
/tmp

/mnt
/mnt


/tmp

02-Shell 编程之变量
https://flepeng.github.io/021-Shell-02-Shell-编程之变量/
作者
Lepeng
发布于
2016年1月1日
许可协议