Shell的变量功能
目录
1、什么是变量
(1)变量的可变性与方便性
(2)影响bash环境操作的变量-环境变量
(3)脚本程序设计(shell script)的好帮手
2、变量的取用与设定:echo,变量设定规则,unset
(1)变量的取用echo与变量的设定
(2)变量的设定规则
(3)设定规则举例:export 子bash继承父bash的变量
3、环境变量的功能
(1)用env观察环境变量与常见的环境变量说明
(2)用set观察所有变量(环境变量、自定义变量、shell内部变量)
(3)补充说明:环境变量、系统变量、用户变量的关系
(4)export:自定义变量转为环境变量
4、影响显示结果的语系变量(locale)
(1)通过变量修订我们的编码:locale查看变量
5、变量的有效范围
6、变量键盘读取、数组与宣告:read,array,declare
(1)read指令:读取来自键盘输入的变量
(2)declare / typeset
(3)数组array变量类型:
7、与文件系统及程序的限制关系:ulimit
8、变量内容的删除、取代与替换(Optional)
(1)变量内容的删除与取代:#(##);%(%%);/(//)
(2) 变量的测试与内容替换
Linux 是多人多任务的环境,每个人登入系统都能取得一个 bash shell, 每个人都能够使用 bash 下达 mail 这个指令来收受『自己』的邮件等等。问题是, bash 是如何得知你的邮件信箱是哪个文件? 这就需要『变量』的帮助啦!底下我们将介绍重要的环境变量、变量的取用与设定等内容。
1、什么是变量
用一个简单的“字眼”或者名称来取代另一个比较复杂或者容易变动的数据(例如username在不同用户登录时不同,但是都是使用username来表示当前用户)
(1)变量的可变性与方便性
我们每个账号的邮箱都是用MAIL这个变量来进行存取的,当langxi这个用户登录时,就会取得MAIL这个变量,而这个变量的内容就是/var/spool/mail/langxi。当我们使用信件读取指令mail读取自己的邮箱时,这个程序就直接读取MAIL的内容,从而找到自己的邮箱。
系统已经帮我们规划好 MAIL 这个变量,所以用户只要知道 mail 这个指令如何使用即可, mail 会主动的取用 MAIL 这个变量。
(2)影响bash环境操作的变量-环境变量
- 你能不能在任何目录下执行某个指令,与 PATH 这个变量有很大的关系。例如你下达 ls 这个指令时,系统就是透过 PATH 这个变量里面的内容所记录的路径顺序来搜寻指令的!如果在搜寻完 PATH 变量内的路径还找不到 ls 这个指令时, 就会在屏幕上显示『 command not found 』的错误 讯息了。
- 在进入 shell 之前,也正如同 上面提到的,由于系统需要一些变量来提供他数据的存取 (或者是一些环境的设定参数值, 例如是 否要显示彩色等等的) ,所以就有一些所谓的『环境变量』 需要来读入系统中了!这些环境变量例 如 PATH、HOME、MAIL、SHELL 等等,都是很重要的, 为了区别与自定义变量的不同,环境变量通常以大写字符来表示呢!
(3)脚本程序设计(shell script)的好帮手
- 你要写一个大型的 script 时,有些数据因为可能由于用户习惯的不同而有差异,比如说路径好了,由于该路径在 script 被 使用在相当多的地方,如果下次换了一部主机,都要修改 script 里面的所有路径,那么我一定会疯掉!
- 这个时候如果使用变量,而将该变量的定义写在最前面,后面相关的路径名称都以变量来取代,嘿嘿!那么你只要修改一行就等于修改整篇 script 了!
2、变量的取用与设定:echo,变量设定规则,unset
(1)变量的取用echo与变量的设定
echo ${SHELL}
/bin/bash
- 变量的取用就如同上面的范例,利用 echo 就能够读出,只是需要在变量名称前面加上 $ , 或者是以 ${变量} 的方式来取用都可以。
- 至于变量的设定:用『等号(=)』连接变量与他的内容就好啦!举例来说: 我要将 myname 这个变量名称的内容设定为 langxi
myname=langxi
echo ${myname}
langxi
(每一种 shell 的语法都不相同~在变量的使用上,bash 在你没有设定的变量中强迫去 echo 时,它会显示出空的值。 在其他某些 shell 中,随便去 echo 一个不存在的变量,它是会出现错误讯息的喔!要注意!要注意)
(2)变量的设定规则
- 变量与内容之间以‘=’来连接,‘=’两边不能直接接空格
myname=langxi echo ${myname} # langxi myname= langxi # -bash: langxi: command not found myname = langxi # -bash: myname: command not found
- 变量名称只能时英文字母或者数字,并且不能以数字开头
- 变量内容如果有空格符,可使用双引号【"】或者单引号【'】将变量内容结合起来(类似于字符串)但$在双引号时可保留原本变量的特性
o 双引号内的特殊字符如 $ 等,可以保有原本的特性,如下所示: 『
var="lang is $LANG"』则『echo $var』可得『lang is zh_TW.UTF-8』
o 单引号内的特殊字符则仅为一般字符 (纯文本),如下所示:
『var='lang is $LANG'』则『echo $var』可得『lang is $LANG』
- 可用跳脱字符『 \ 』将特殊符号(如 [Enter], $, \, 空格符, '等)变成一般字符,如:『myname=VBird\ Tsai』
myname=langxi\ angel
echo ${myname}
# langxi angel
- 在一串指令的执行中,还需要藉由其他额外的指令所提供的信息时,可以使用反单引号『`指令`』或 『$(指令)』。特别注意,那个 ` 是键盘上方的数字键 1 左边那个按键,而不是单引号! 例如想要取得核心版本 的设定:
version=$(uname -r)
echo $version
# 3.10.0-1160.119.1.el7.x86_64
- 若该变量为扩增变量内容时,则可用 "$变量名称" 或 ${变量} 累加内容,如下所示『PATH="$PATH":/home/bin』或『PATH=${PATH}:/home/bin』
echo ${PATH}
# /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
PATH=${PATH}:/home/bin
echo ${PATH}
# /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/home/bin
- 若该变量需要在其他子程序执行,则需要以 export 来使变量变成环境变量: 『export PATH』
- 通常大写字符为系统默认变量,自行设定变量可以使用小写字符,方便判断 (纯粹依照使用者兴趣与嗜好) ;
- 取消变量的方法为使用 unset :『unset 变量名称』例如取消 myname 的设定:『unset myname』
echo $myname
# langxi angel
unset myname
echo $myname[root@ptivitic ~]#
(3)设定规则举例:export 子bash继承父bash的变量
- 在一般的状态下,父程序的自定义变量是无法在子程序内使用的。但是透过 export 将变量变成环境变量后,就能够在子程序底下应用了
[root@ptivitic ~]# myname=langxi\ angel
[root@ptivitic ~]# echo ${myname}
langxi angel
[root@ptivitic ~]# bash
[root@ptivitic ~]# echo ${myname}[root@ptivitic ~]# exit
exit
[root@ptivitic ~]# echo ${myname}
langxi angel
[root@ptivitic ~]# export myname
[root@ptivitic ~]# bash
[root@ptivitic ~]# echo ${myname}
langxi angel
- 每个 Linux 都能够拥有多个核心版本,且几乎 distribution 的核心版本都不相同。以 CentOS 7.1 (未 更新前) 为例,他的预设核心版本是 3.10.0-229.el7.x86_64 ,所以核心模块目录在 /lib/modules/3.10.0-229.el7.x86_64/kernel/ 内。 也由于每个 distributions 的这个值都不相同,但是我 们却可以利用 uname -r 这个指令先取得版本信息。
cd /lib/modules/$(uname -r)/kernel
ls
arch crypto drivers fs kernel lib mm net sound virt
3、环境变量的功能
(1)用env观察环境变量与常见的环境变量说明
- HOSTNAME:主机名
- HOME: 当前用户的家目录,可直接通过cd~切换到家目录
- SHELL:当前环境使用的SHELL是哪个程序
- HISTSIZE: 历史指令可记录的最大数量
- PATH: 执行文件搜寻的路径~目录与目录中间以冒号(:)分隔,由于文件的搜寻是依序由 PATH 的变量内的目录来查询,所以,目录的顺序也是重要的喔。
- LANG: 语系数据啰~很多讯息都会用到他, 举例来说,当我们在启动某些 perl 的程序语言文件 时,他会主动的去分析语系数据文件, 如果发现有他无法解析的编码语系,可能会产生错误。中文编码通常是 zh_TW.Big5 或者是 zh_TW.UTF-8。
(2)用set观察所有变量(环境变量、自定义变量、shell内部变量)
一般来说,不论是否为环境变量,只要跟我们目前这个 shell 的操作接口有关的变量,通常都会被设定为大写字符,也就是说,『基本上,在 Linux 预设的情况中,使用{大写的字母}来设定的变量 一般为系统内定需要的变量』。
- PS1(命令提示字符的设定):当我们每次按 下 [Enter] 按键去执行某个指令后,最后要再次出现提示字符时,就会主动去读取这个变数值了。 上头 PS1 内显示的是一些特殊符号,这些特殊符号可以显示不同的信息,使用echo ${PS1}查看当前变量的值。
如果想显示完整的工作目录:直接修改PS1的值
[root@ptivitic ~]#echo ${PS1}
[\u@\h \W]\$
[root@ptivitic ~]#PS1='[\u@\h \w]\$'
[root@ptivitic ~]#mkdir test
[root@ptivitic ~]#cd test
[root@ptivitic ~/test]#
- $(关于本shell的PID):$本身也是一个变量,代表当前shell的PID
[root@ptivitic /]# echo ${$}
9412
- ?(关于上个执行指令的回传值):当 我们执行某些指令时,这些指令都会回传一个执行后的代码。一般来说,如果成功的执行该指令, 则会回传一个 0 值,如果执行过程发生错误,就会回传『错误代码』才对!一般就是以非为 0 的 数值来取代。
# 以数字开头命名变量的错误
[root@ptivitic /]# 1sx=as
-bash: 1sx=as: command not found
[root@ptivitic /]# echo ${?}
127
[root@ptivitic /]# echo ${?}
0
(3)补充说明:环境变量、系统变量、用户变量的关系
首先,我来说一下他们之间的关系:环境变量只是一个总称,代表了系统变量和用户变量,因此我们说环境变量都是指的系统变量和用户变量。系统变量就是系统级别的变量,用户需要使用系统变量。如果系统变量被修改了,而任何系统用户都在用系统变量,因此每个系统用户都将受到影响。用户变量运行在系统变量之上的,每个用户拥有不同的用户变量,不同用户的用户变量之间是并列的,也是互不干扰的。他们之间的关系图如下如所示
(4)export:自定义变量转为环境变量
谈了 env 与 set 现在知道有所谓的环境变量与自定义变量,那么这两者之间有啥差异呢?其实这两 者的差异在于『 该变量是否会被子程序所继续引用』
当你登入 Linux 并取得一个 bash 之后,你的 bash 就是一个独立的程序,这个程序的识别使用的是 一个称为程序标识符,被称为 PID 。 接下来你在这个 bash 底下所下达的任何指令都是由这 个 bash 所衍生出来的,那些被下达的指令就被称为子程序了。
子程序仅会继承父程序的环境变量, 子程序不会继承父程序的自定义变量;如果仅下达 export 而没有接变量时,那么此时将会把所有的『环境变量』秀出来。
[root@ptivitic /]# myname=zhangsan
[root@ptivitic /]# bash
[root@ptivitic /]# echo ${myname}[root@ptivitic /]# exit
exit
[root@ptivitic /]# echo ${myname}
zhangsan
[root@ptivitic /]# export myname
[root@ptivitic /]# bash
[root@ptivitic /]# echo ${myname}
zhangsan
4、影响显示结果的语系变量(locale)
目前大多数的 Linux distributions 已经都是支持日渐流行的万国码了,也都支持大部分的国家语系。 那么我们的 Linux 到底支持了多少的语系呢?这可以由 locale 这个指令来查询到喔!
(1)通过变量修订我们的编码:locale查看变量
事实上,如果其他的语系变量都未设定, 且你有设定 LANG 或者是 LC_ALL 时,则其他的语系变量就会被这两个变量所取代! 这也是为什么 我们在 Linux 当中,通常说明仅设定 LANG 或 LC_ALL 这两个变量而已,因为他是最主要的设定变量!
LANG=en_US.UTF-8 如果是这个语言环境,也能够支持有utf-8编码的中文
整体系统默认的语系定义在 /etc/locale.conf
cat /etc/locale.conf
LANG=en_US.UTF-8
5、变量的有效范围
如果在跑程序的时候,有父程序与子程序的不同程序关系时,则『变量』可否被引用与 export 有关。 被 export 后的变量,我们可以称他为『环境变量』! 环境变量可以被子程序所引用,但是其他的自 定义变量内容就不会存在于子程序中。
- 当启动一个 shell,操作系统会分配一记忆区块给 shell 使用,此内存内的变量可让子程序取用
- 若在父程序利用 export 功能,可以让自定义变量的内容写到上述的记忆区块当中(环境变量);
- 当加载另一个 shell 时 (亦即启动子程序,而离开原本的父程序了),子 shell 可以将父 shell 的环境变量所在的记忆区块导入自己的环境变量区块当中。
6、变量键盘读取、数组与宣告:read,array,declare
(1)read指令:读取来自键盘输入的变量
选项与参数:
-p :后面可以接提示字符!
-t :后面可以接等待的『秒数!』这个比较有趣~不会一直等待使用者啦!
[root@ptivitic locale]# read myname
zhangsan
[root@ptivitic locale]# echo ${myname}
zhangsan
[root@ptivitic locale]# read -p "please input your name:" -t 10 myname
please input your name:lisi
[root@ptivitic locale]# echo ${myname}
lisi
(2)declare / typeset
- 变量类型默认为『字符串』,所以若不指定变量类型,则 1+2 为一个『字符串』而不是『计算式』。
- bash 环境中的数值运算,预设最多仅能到达整数形态,所以 1/3 结果是 0;
declare 或 typeset 是一样的功能,就是在『宣告变量的类型』。如果使用 declare 后面并没有接任何 参数,那么 bash 就会主动的将所有的变量名称与内容通通叫出来,就好像使用 set 一样
选项与参数:
-a :将后面名为 variable 的变量定义成为数组 (array) 类型
-i :将后面名为 variable 的变量定义成为整数数字 (integer) 类型
-x :用法与 export 一样,就是将后面的 variable 变成环境变量;
-r :将变量设定成为 readonly 类型,该变量不可被更改内容,也不能 unset
+x :由环境变量变为自定义变量
[root@ptivitic locale]# echo ${sum}
10+30+50
[root@ptivitic locale]# declare -i sum=10+30+50
[root@ptivitic locale]# echo ${sum}
90[root@ptivitic locale]# declare -x sum
[root@ptivitic locale]# export | grep sum
declare -ix sum="90"
[root@ptivitic locale]# declare -r sum
[root@ptivitic locale]# sum=10
-bash: sum: readonly variable
[root@ptivitic locale]# declare +x sum
[root@ptivitic locale]# export | grep sum
[root@ptivitic locale]# declare -p sum
declare -ir sum="90"
(3)数组array变量类型:
范例:设定 var[1] ~ var[3] 的变量。
[dmtsai@study ~]$ var[1]="small min"
[dmtsai@study ~]$ var[2]="big min"
[dmtsai@study ~]$ var[3]="nice min"
[dmtsai@study ~]$ echo "${var[1]}, ${var[2]}, ${var[3]}"
small min, big min, nice min
7、与文件系统及程序的限制关系:ulimit
想象一个状况:我的 Linux 主机里面同时登入了十个人,这十个人不知怎么搞的, 同时开启了 100 个文件,每个文件的大小约 10MBytes ,请问一下, 我的 Linux 主机的内存要有多大才够? 10*100*10 = 10000 MBytes = 10GBytes ... 老天爷,这样,系统不挂点才有鬼哩!
为了要预防这个情况的发生,所以我们的 bash 是可以『限制用户的某些系统资源』的,包括可以开启的文件数量, 可以使用的 CPU 时间,可以使用的内存总量等等。如何设定?用 ulimit 吧!
[dmtsai@study ~]$ ulimit [-SHacdfltu] [配额]
选项与参数:
-H :hard limit ,严格的设定,必定不能超过这个设定的数值;
-S :soft limit ,警告的设定,可以超过这个设定值,但是若超过则有警告讯息。在设定上,通常 soft 会比 hard 小,举例来说,soft 可设定为 80 而 hard设定为 100,那么你可以使用到 90 (因为没有超过 100),但介于 80~100 之间时,系统会有警告讯息通知你!
-a :后面不接任何选项与参数,可列出所有的限制额度;
-c :当某些程序发生错误时,系统可能会将该程序在内存中的信息写成文件(除错用),这种文件就被称为核心文件(core file)。此为限制每个核心文件的最大容量。
-f :此 shell 可以建立的最大文件容量(一般可能设定为 2GB)单位为 Kbytes
-d :程序可使用的最大断裂内存(segment)容量;
-l :可用于锁定 (lock) 的内存量
-t :可使用的最大 CPU 时间 (单位为秒)
-u :单一用户可以使用的最大程序(process)数量。范例一:列出你目前身份(假设为一般账号)的所有限制数据数值
[dmtsai@study ~]$ ulimit -a
core file size (blocks, -c) 0 <==只要是 0 就代表没限制
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited <==可建立的单一文件的大小
pending signals (-i) 4903
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024 <==同时可开启的文件数量
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 4096
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited范例二:限制用户仅能建立 10MBytes 以下的容量的文件
[dmtsai@study ~]$ ulimit -f 10240
[dmtsai@study ~]$ ulimit -a | grep 'file size'
core file size (blocks, -c) 0
file size (blocks, -f) 10240 <==最大量为 10240Kbyes,相当 10Mbytes
[dmtsai@study ~]$ dd if=/dev/zero of=123 bs=1M count=20
File size limit exceeded (core dumped) <==尝试建立 20MB 的文件,结果失败了!
[dmtsai@study ~]$ rm 123 <==赶快将这个文件删除啰!同时你得要注销再次的登入才能解开 10M 的限制
如果想要让使用者建立的文件不要太 大时, 我们是可以考虑用 ulimit 来限制使用者可以建立的文件大小喔!利用 ulimit -f 就可以来设 定了!例如上面的范例二,要注意单位喔!单位是 Kbytes。 若改天你一直无法建立一个大容量的文 件,记得瞧一瞧 ulimit 的信息喔!
要复原 ulimit 的设定最简单的方法就是注销再登入,否则就是得要重新以 ulimit 设 定才行! 不过,要注意的是,一般身份使用者如果以 ulimit 设定了 -f 的文件大小, 那么他『只能继续减小文件 容量,不能增加文件容量喔!』
8、变量内容的删除、取代与替换(Optional)
通过简单的动作来将变量的内容进行微调
(1)变量内容的删除与取代:#(##);%(%%);/(//)
# :符合取代文字的『最短的』那一个;
##:符合取代文字的『最长的』那一个
(#从头开始到尾部)[root@localhost ~]# path=${PATH}
[root@localhost ~]# echo ${path}
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@localhost ~]# echo ${path#/*sbin:}
/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@localhost ~]# echo ${path##*:}
/root/bin% :符合取代文字的『最短的』那一个
%%:符合取代文字的『最长的』那一个
(从尾部开始到头部)
[root@localhost ~]# echo ${path}
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@localhost ~]# echo ${path%:*bin}
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
[root@localhost ~]# echo ${path%%:*bin}
/usr/local/sbin/ :新字符取代第一个旧字符
//:新字符取代全部的旧字符
[root@localhost ~]# echo ${path}
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@localhost ~]# echo ${path/sbin/SBIN}
/usr/local/SBIN:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@localhost ~]# echo ${path//bin/SBIN}
/usr/local/sSBIN:/usr/local/SBIN:/usr/sSBIN:/usr/SBIN:/root/SBIN
[root@localhost ~]#
(2) 变量的测试与内容替换
在某些时刻我们常常需要『判断』某个变量是否存在,若变量存在则使用既有的设定,若变量不存在则给予一个常用的设定。
在上面的范例中,重点在于减号『 - 』后面接的关键词!这里先测试旧变量是否存在,如果不存在则用”-“后的内容来取代。
不过这还是有点问题!因为 username 可能已经被设定为『空字符串』了!果真如此的话,那你还可以使用底下的范例来给予 username 的内容成为 root 喔!
加上冒号后,被测试的变量未被设定或者是已被设定为空字符串时, 都能够用后面的内容 (本例中是使用 root 为内容) 来替换与设定!
这里给出其他的测试方法: