文章目录
- shell的变量功能
- 什么是变量?
- 变量的可变性与方便性
- 影响bash环境操作的变量
- 脚本程序设计(shell script)的好帮手
- 变量的使用与设置:echo、变量设置规则、unset
- 变量的使用(echo)
- 变量设置的规定
- 使用案例
- 环境变量的功能
- 用env观察环境变量与常见环境变量说明
- 用set观察所有变量(含环境变量与自定义变量)
- PS1(提示字符的设置)
- $(关于本shell的PID)
- ?(关于上一个执行命令的返回值)
- export(自定义变量转成环境变量)
- 影响显示结果的语系变量(locale)
- 变量的有效范围
- 变量键盘读取、数组与声明:read、array、declare
- read
- 使用案例
- declare,typeset
- 使用案例
- 数组(array)变量类型
- 与文件系统及程序的限制关系
- 使用案例
- 变量内容的删除、取代与替换
- 变量内容的删除与替换
- 变量的测试与内容替换
shell的变量功能
变量是bash环境中非常重要的一个东西,我们知道Linux是多人多任务的环境,每个人登录系统都能取得一个bash shell,每个人都能够使用bash执行mail这个命令来接受自己的邮件等。bash如何得知你的邮箱是哪个文件的?这就需要变量的帮助。
什么是变量?
什么是变量呢?简单来说,就是让某一个特定字符串代表不固定的内容。
举例来说就像【y=ax+b】这东西,在等号左边的(y)是变量,在等号右边(ax + b)是变量内容,需要注意的是左边是未知数,右边是已知数。
变量的可变性与方便性
举例来说,我们每个账号的邮箱默认是以MAIL这个变量来进行存取的,当csq这个用户登录的时候,它便会取得MAIL这个变量,而这个变量的内容其实就是/var/spool/mail/csq,如果zhw登录呢?它取得的MAIL这个变量的内容其实就是/var/spool/mail/zhw。而我们使用邮件读取命令mail,来读取自己的邮箱时,这个程序可以直接读取MAIL这个变量的内容,这样就能够自动地分辨处属于自己的邮箱。
想要了解mail可以访问 : https://blog.csdn.net/qq_52089863/article/details/130175946?spm=1001.2014.3001.5501
由于系统已经帮我们规划好MAIL这个变量,所以用户只要知道mail这个命令如何使用即可,mail会主动使用MAIL这个变量,如上图。
影响bash环境操作的变量
某些特定变量会影响到bash的环境。举例来说,你可以使用【echo ${PATH}】打印一下PATH变量,
会出现一堆目录里面放的可执行文件。你能不能在任何目录下执行命令,与PATH这个变量有很大的关系。例如你执行【ls】这个命令时,系统就是通过PATH这个变量里面的内容所记录的路径顺序来查找命令。如果说PATH变量内的路径没找到【ls】这个命令,那么就会在屏幕上显示【command not found】的错误信息,就是未找到命令的意思。
那么系统默认的变量除了PATH还有哪些呢?
-
HOME:用户的主目录,通常是 /home/username。
-
SHELL:默认的Shell程序。
-
USER:当前登录的用户名。
-
PWD:当前所在的工作目录。
-
LANG:系统默认语言。
-
TERM:终端类型。
-
PS1:Shell提示符。
-
PS2:Shell多行输入时的提示符。
-
DISPLAY:X Window的显示器名称。
-
EDITOR:默认的文本编辑器。
-
HISTSIZE:历史记录的大小。
-
HOSTNAME:当前主机的名称。
-
MAIL:邮件的存储路径。
-
SHLVL:表示Shell的嵌套深度,每次启动Shell时增加1。
脚本程序设计(shell script)的好帮手
这些还都只是系统默认的变量的目的,如果是个人的设置方面的应用:例如你要写一个大型脚本时,有些数据因为可能由于用户习惯的不同而有差异,比如说路径,由于该路径在脚本被使用在相当多的地方,如果下次换一台主机,都笑修改脚本里面所有路径,那么一定很麻烦。这个时候如果使用变量,而将该变量的定义写在最前面,后面相关的路径名称都以变量来替换,那么你只要修改一行就等于修改了整篇脚本,非常方便。
简单来说变量就是一组文件或符号等,来替换一些设置或一串保留的数据,例如:我设置了【myname】就是【csq】,所以当你读取了【myname】这个变量时,系统自然就会知道,那就是【csq】,那么如何显示变量呢?这就需要用到【echo】这个命令
变量的使用与设置:echo、变量设置规则、unset
你可以利用echo这个命令来使用变量,但是,变量在被使用时,前面必须要加上【$】符号才行,假如你想要知道PATH内容
变量的使用(echo)
[root@localhost ~]# echo ${PATH}
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/.local/bin:/root/bin
利用echo就能够读出,只需要在变量名称前面加上
,或是以
,或是以
,或是以{变量}的方式使用都可以(推荐使用${变量}
。
现在我们知道了变量与变量内容之前的相关性了,那么我要如何设置或是修改某个变量的内容呢?很简单,用等号(=)连接变量与它的内容就好,举例来说:我要将myname这个变量名称的内容设置为CSQ该如何设置呢?:
[root@localhost ~]# echo ${myname}
<== 这里没有显示任何数据,因为这个变量尚未被设置,是空的。
[root@localhost ~]# myname=CSQ
[root@localhost ~]# echo ${myname}
CSQ <== 出现了,因为这个变量已经被设置了
在bash当中,当一个变量名称尚未被设置时,默认的内容是【空】,另外,变量在设置时,还是需要符合某些规定的,否则会设置失败。
变量设置的规定
- 变量与变量内容以一个等号【=】来连接
例如:myname=CSQ
- 等号两边不能直接接空格
例如:myname = CSQ
- 变量名称只能是英文字母与数字,但是开头字符不能是数字
例如:2myname=CSQ
- 变量内容若有空格可以使用双引号【"】或单引号【'】将变量内容结合起来,但双引号内的特殊字符如【$】等可以保有原本的特性
例如:yuyan=“lang is $LANG” 则echo ${yuyan}可得lang is zh_CN.UTF-8
单引号内的特殊字符则仅为一般字符(纯文本)
例如:yuyan=‘lang is $LANG’ 则 echo ${yuyan}可得lang is $LANG
-
`可用转义符【\】将特殊符号如([Enter]、$、\、空格、’ 等)变成一般字符,
如:myname=zhw\ Tsai 就可以打印出空格 -
在一串命令的执行中,还需要借由其他额外的命令所提供的信息时,可以使用反单引号【`命令`】或【$(命令)】。特别注意,那个 ` 是键盘上方数字键 1 左边的那个按键,而不是单引号。
例如你想取得内核版本的信息:version=$(uname -r) 再输入 echo ${version} 可得内核版本
- 若该变量为扩增变量内容时,则可用" 变量名称 " 或 变量名称"或 变量名称"或{变量}累加内容
例如:PATH=" P A T H " : / h o m e / b i n 或 P A T H = PATH":/home/bin 或 PATH= PATH":/home/bin或PATH={PATH}:/home/bin
- 若该变量需要在其他子程序执行,则需要以export来使变量变成环境变量
例如:export PATH
-
通常大写字符为系统默认变量,自行设置变量可以使用小写字符,方便判断
-
取消变量的的方法使用unset:【unset 变量名称】
例如:取消myname的设置 unset myname
上数内容中我们谈到子进程,那什么是子进程呢?就是说,在目前这个shell的情况下,去启用另一个新的shell,新的那个shell就是子进程。在一般的状态下,父进程的自定义变量是无法在子进程内使用的,但是通过export将变量变成环境变量后,就能够在子进程下面使用。
使用案例
设置变量name,且内容为csq
[root@localhost kernel]# 12name=csq
-bash: 12name=csq: 未找到命令 # 不能以数字开头
[root@localhost kernel]# name = csq
-bash: name: 未找到命令 # 还是错的,等号两边不能有空格
[root@localhost kernel]# name=csq
[root@localhost kernel]# echo ${name}
csq # 这样才是成功的
若name的变量内容为CSQ’s name 就是变量内容含有特殊符号时:
[root@localhost kernel]# name=CSQ's name
# 单引号与双引号必须要成对 ,在上面的设置中只要一个单引号,因此你按下回车后
# 你还可以继续输入变量内容,这与我们需要的功不同
# 失败后按ctrl + c 结束进程
[root@localhost kernel]# name="CSQ's name"
# 在引号里面的单引号成为了一般字符,为什么不用单引号呢,因为单引号与内容中的单引号凑成了一对就不是原本的内容了
[root@localhost kernel]# echo $name
CSQ's name
[root@localhost kernel]# name=CSQ\'s\ name # 利用转义字符把单引号和空格转义也是可以的
[root@localhost kernel]# echo $name
CSQ's name
我要在PATH这个变量中【累加】:/home/csq这个目录
[root@localhost kernel]# PATH=${PATH}:/home/csq # 有很多种方法这里只举例一种
如果我要将name变量里面加一个yes呢?
[root@localhost kernel]# name=${name}yes
[root@localhost kernel]# echo $name
CSQ's nameyes
如何让我刚刚设置的name=csq可以用在下一个shell程序?
[root@localhost kernel]# name=csq # 设置变量
[root@localhost kernel]# bash # 进入子进程
[root@localhost kernel]# echo $name # 进入后echo一下
# 你会发现变量没有被设置
[root@localhost kernel]# exit # 退出子进程
[root@localhost kernel]# export name # 设置环境变量
[root@localhost kernel]# bash # 进入子进程
[root@localhost kernel]# echo $name # echo一下
csq # 出现设置值了
[root@localhost kernel]# exit # 退出子进程
如何进入到内核模块目录?
[root@localhost kernel]# cd /lib/modules/$(uname -r)/kernel
[root@localhost kernel]# cd /lib/modules/`uname -r`/kernel
# 两种方法都是可行的,一般采用第一种
取消我们刚刚设置的name的变量
[root@localhost kernel]# unset name
环境变量的功能
环境变量可以帮我们实现很多功能,包括根目录(主文件夹)的变换、提示字符的显示、执行文件查找的路径等,还有很多很多。那么,既然环境变量有那么多功能,目前我的shell环境种,有多少默认的环境变量呢?我们可以利用两个命令查看,【env】与【export】
用env观察环境变量与常见环境变量说明
列出目前shell环境下的所有环境变量
[root@localhost ~]# env
XDG_SESSION_ID=1
HOSTNAME=localhost.localdomain # 这台主机的主机名称
SELINUX_ROLE_REQUESTED=
TERM=xterm # 这个终端使用的环境是什么类型
SHELL=/bin/bash # 目前这个环境下,使用的shell是哪个程序
HISTSIZE=1000 # history命令在Centos默认可记录1000条
SSH_CLIENT=192.168.100.1 50146 22
SELINUX_USE_CURRENT_RANGE=
OLDPWD=/lib/modules/3.10.0-1160.el7.x86_64/kernel
SSH_TTY=/dev/pts/0
USER=root # 使用者的名称
MAIL=/var/spool/mail/root
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/home/csq
PWD=/root
LANG=zh_CN.UTF-8 # 这个与语系有关
SELINUX_LEVEL_REQUESTED=
HISTCONTROL=ignoredups
SHLVL=1
HOME=/root # 使用者的家目录
LOGNAME=root # 登录者用来登录的账号名称
SSH_CONNECTION=192.168.100.1 50146 192.168.100.10 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
XDG_RUNTIME_DIR=/run/user/0
_=/usr/bin/env # 上一次使用命令的最后一个参数
那么上面的变量有什么功能呢?我们来介绍一下(这个内容在影响bash环境操作的变量种谈到过)
- HOME
代表用户的根目录
- SHELL
告诉我们,目前这个环境是的SHELL是哪个程序
- HISTSIZE
这个与历史命令有关,就是我们曾经执行过的命令会被系统记录下来,在之前的博客history中讲过
当我们使用mail这个命令在收信时,系统会去读取的邮箱文件(mailbox)
- PATH
就是执行文件查找的路径,目录与目录中间以冒号【:】分割,由于文件的查找是依序由PATH的变量内的目录查询的,所以目录的顺序也很重要
- LANG
LANG变量是一个环境变量,用于指定当前系统所使用的默认语言和字符集。
在Linux和Unix系统中,LANG变量通常是在/etc/profile这个文件中定义的,它的值通常是一个类似于“en_US.UTF-8”的字符串,其中“en_US”表示语言,而“UTF-8”表示字符集
- RANDOM
RANDOM是一个内置的环境变量,用于在Shell脚本中生成随机数。它生成的随机数是一个0到32767之间的整数。在每次调用时,它都会生成一个新的随机数。
使用RANDOM变量非常简单。只需要在脚本中使用$RANDOM即可。以下是一个生成随机数的例子:
[root@localhost 16:22:16 ~]# declare -i number=$RANDOM*10/32768+1 ;echo ${number}
在上面的例子中,declare -i number=$RANDOM*10/32768+1 会生成一个介于1到10之间的随机整数。可以使用这个随机数来进行各种操作,比如生成密码、随机选择列表中的选项等。
需要注意的是,RANDOM变量只能生成整数,如果需要生成小数,则需要进行一些额外的处理。
用set观察所有变量(含环境变量与自定义变量)
bash可不止有环境变量,还有一些与bash操作界面有关的变量,以及用户自己定义的变量存在。那么这些变量如何观察?这时候可以用set命令。set除了环境变量除外,还会将其他在bash内的变量通通显示出来,下面列举一部分内容。
[root@localhost ~]# set
BASH=/bin/bash # bash的主程序路径
BASH_VERSINFO=([0]="4" [1]="2" [2]="46" [3]="2" [4]="release" [5]="x86_64-redhat-linux-gnu")
BASH_VERSION='4.2.46(2)-release' # 这两行是bash的版本
COLUMNS=109 # 在目前的终端环境下,使用的栏位有几个字符长度
HISTFILE=/root/.bash_history # 历史命令记录的放置文件,隐藏文件
HISTFILESIZE=1000 # 历史命令文件最大记录数1000(存在上面这个变量文件里)
HISTSIZE=1000 # 内存中记录历史命令条数最大1000条
IFS=$' \t\n' # 默认的分隔符号
LINES=24 # 目前终端下的最大行数
MACHTYPE=x86_64-redhat-linux-gnu # 安装的机器类型
OSTYPE=linux-gnu # 操作系统类型
PS1='[\u@\h \W]\$ ' # 命令提示符
PS2='> ' # 如果你使用转义字符(\),这是第二行以后的提示字符
$ # 目前中shell所使用的PID
? # 刚刚执行完命令的返回值
.......
....
...
PS1(提示字符的设置)
PS1就是我们的命令提示符。当我们每次按下[Enter]按键去执行某个命令后,最后要再次出现提示字符时,就会主动去读取整个变量值。上面PS1内显示的是一些特殊符号,这些特殊符号可以显示不同的信息。下面我们来介绍一下
-
\d:显示出【星期 月 日】 的日期格式,如【Mon Feb 2】
-
\H:完整的主机名。举例来说【localhost.localdomain】
-
\h:仅取主机名在第一个小数点前的名字,如【localhost】
-
\t:显示时间,为24小时格式的【HH:MM:SS】
-
\T:显示时间,为12小时格式的【HH:MM:SS】
-
\A:显示时间,为24小时格式的【HH:MM】
-
@:显示数据,为12小时格式的【am/pm】
-
\u:目前用户的账户名称,如【csq】
-
\v:BASH的版本信息
-
\w:完整的工作目录名称,由根目录写起的目录名称,单根目录会以~替换
-
\W:利用basename函数取得工作目录名称,所以仅会列出最后一个目录名
-
\# :执行的第几个命令
-
\KaTeX parse error: Expected 'EOF', got '#' at position 21: …,如果是root时,提示字符为#̲,否则就是
让我们来看看Centos默认的PS1内容吧【[\u@\h \W]\$】现在你知道那些反斜杠后的参数的意义了吧?要注意,那个反斜杠后的参数为PS1的特殊功能,与bash的变量设置没关系,不要搞混了,那你现在知道了为何你的命令提示字符是【[root@localhost ~]# 】了吧?
那么假设我想要给命令提示字符添加一个显示时间为24小时格式的【HH:MM:SS】该怎么添加呢?
[root@localhost ~]# PS1='[\u@\h \W \t]\$'
[root@localhost ~ 13:49:49]#
# 看到了吗,提示字符变了
$(关于本shell的PID)
美元符本身也是一个变量。整个东西代表的是目前这个shell的进程号,就是所谓的PID。想要知道我们的shell的PID,就可以用【echo $$】,就会出现我们shell的PID
[root@localhost ~ 13:53:42]# echo $$
9937
?(关于上一个执行命令的返回值)
问好也是一个特殊的变量,在bash里面这个变量很重要。这个变量是上一个执行的命令所返回的值。
当我们执行某个命令的时候,这些命令都会返回一个执行后的代码。一般来说执行成功会返回一个0值,如果执行过程中失败,就会返回错误的代码,一般就是非0的数值来替换
[root@localhost ~ 14:03:47]# echo $SHELL
/bin/bash # 可以顺利执行没有错误
[root@localhost ~ 14:04:02]# echo $?
0 # 没有错误所以返回0
[root@localhost ~ 14:04:06]# 1name=csq
bash: 1name=csq: 未找到命令 # 发生错误了
[root@localhost ~ 14:04:21]# echo $?
127 # 因为发生了错误,返回错误(非0)
- OSTYPE,HOSTTYPE,MACHTYPE(主机硬件与内核的等级)
- OSTYPE(操作系统类型)
OSTYPE是一个环境变量,它指示当前操作系统的类型。这个变量在不同的操作系统中有不同的名称,例如,在Linux中,它被称为 O S T Y P E ,在 M a c O S 中,它被称为 OSTYPE,在MacOS中,它被称为 OSTYPE,在MacOS中,它被称为SYSTEM_TYPE。OSTYPE的值通常是一个字符串,表示操作系统的名称和版本号,例如,linux-gnu、darwin、win32等。- HOSTTYPE(主机类型)
HOSTTYPE也是一个环境变量,它指示当前主机的类型。这个变量在不同的操作系统中有不同的名称,例如,在Linux中,它被称为 H O S T T Y P E ,在 M a c O S 中,它被称为 HOSTTYPE,在MacOS中,它被称为 HOSTTYPE,在MacOS中,它被称为MACHTYPE。HOSTTYPE的值通常是一个字符串,表示主机的类型和处理器架构,例如,x86_64、i686、armv7等。- MACHTYPE(机器类型)
MACHTYPE是一个环境变量,它指示当前机器的类型。这个变量通常是由HOSTTYPE和其他信息组成的,以提供更详细的硬件和内核等级信息。MACHTYPE的值通常是一个字符串,表示主机的类型、处理器架构、操作系统类型和内核类型等信息,例如,x86_64-pc-linux-gnu、armv7l-unknown-linux-gnueabihf等。
export(自定义变量转成环境变量)
谈了env和set现在知道所谓的环境变量与自定义变量,那么两者之间有啥差异?其实这俩者差异在于【该变量是否会被子进程所继续引用】。什么是子进程?什么是父进程呢?
当你登录Linux并取得一个bash后,你的bash就是一个独立的进程,这个进程的识别使用的是进程标识符,也就是PID。接下来你在这个bash下面所执行的任何命令都是由这个bash衍生出来的,那些被执行的命令就被称为子进程。如下图
如上图所示,我们在原本的bash下面执行另一个bash,结果操作的环境界面就会跑到第二个bash去(就是子进程),那原本的bash就会暂停情况(睡着了sleep),整个目录运行的环境是实践的部分。若要回到原本的bash中去,就只有将第二个bash结束掉(执行 exit 或 logout)才行。
这个进程概念与变量由啥关系?关系很大,因为子进程会继承父进程的环境变量,子进程不会继承父进程的自定义变量。所以你在原本bash的自定义变量进入子进程后就会消失不见,一直到你离开子进程并回到原本的父进程后,这个变量才会出现。
那我们就可以使用export命令,可以让变量内容继续在子进程中使用
export 变量名称
用于共享自己的变量设置给后来调用的文件或其他进程
如果仅执行export而没有接变量时,那么此时就会把所以的环境变量显示出来
[root@localhost ~ 14:10:33]# export
declare -x HISTCONTROL="ignoredups"
declare -x HISTSIZE="1000"
declare -x HOME="/root"
declare -x HOSTNAME="localhost.localdomain"
...........
......
..
影响显示结果的语系变量(locale)
Linux到底支持多少语系呢?可以由locale来查询
[root@localhost ~ 14:45:43]# locale -a
......
...
zh_CN
zh_CN.gb18030 # gbk 中文编码
zh_CN.gb2312
zh_CN.gbk
zh_CN.utf8 # 简体中文编码
....
....
那么我们如何自定义这些编码呢?其实可以通过下面这些变量
[root@localhost ~ 14:46:03]# locale
LANG=zh_CN.UTF-8 # 主语言的环境
LC_CTYPE="zh_CN.UTF-8" # 字符(文字)辨识编码
LC_NUMERIC="zh_CN.UTF-8" # 数字系统的显示信息
LC_TIME="zh_CN.UTF-8" # 时间系统的显示数据
LC_COLLATE="zh_CN.UTF-8" # 字符的比较与排序
LC_MONETARY="zh_CN.UTF-8" # 币值格式的显示等
LC_MESSAGES="zh_CN.UTF-8" # 信息显示的内容,如功能表,错误信息等。
....
...
....
LC_ALL= # 整体语系的环境
基本上,你可以逐一设置每个与语系有关的变量数据,但事实上,如果其他的语系变量都未设置,且你有设置LANG或是LC_ALL,则其他的语系变量就会被这两个变量所替换,这就是我们在Linux中,通常说明仅设置LANG或LC_ALL这两个变量而已,因为它是最主要的设置变量。
当然每个用户可以去调整自己喜欢的语系,系统默认的语系定义在哪里呢?其实就在/etc/locale.conf里面
[root@localhost 15:08:52 ~]# vim /etc/locale.conf
LANG="zh_CN.UTF-8"
系统原本是中文语系,所有显示的数据通通是中文。但为了网页显示的关系,需要将输出转成英文语系才行,但是又不想写入配置文件,毕竟暂时显示使用,应该怎么做
[root@localhost 15:13:00 ~]# LANG=en_US.UTF-8
[root@localhost 15:13:45 ~]# export LC_ALL=en_US.UTF-8
[root@localhost 15:14:00 ~]# locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=en_US.UTF-8
变量的有效范围
变量的有效范围?我们上面的export命令说明中,就提到了这个概念。如果在运行程序的时候,有父进程与子进程的不同进程关系时,则变量可否被引用与export有关,被export后的变量,我可以称它为环境变量,环境变量可以被子进程所引用,但是其他的自定义变量内容就不会存在与子进程中。
为什么环境变量的数据可以被子进程所引用呢?这是因为内存配置的关系
- 当启动一个shell,操作系统会分配一内存区域给shell使用,此内存中的变量可以让子进程使用。
- 若在父进程利用export功能,可以让自定义变量的内容写到上述的内存区域当中(环境变量)
- 当你加载另一个shell时(即启动子进程,而离开原本的父进程)子shell可以将父shell的环境变量所在的内存区域导入自己的环境变量区块当中。
通过这样的关系,我们就可以让某些变量在相关的进程之存在,帮助自己更方便地操作环境。
变量键盘读取、数组与声明:read、array、declare
read
要读取来自键盘输入的变量,就是要使用read这个命令。这个命令最常被用在shell脚本的编写当中,想要跟用户交互?用这个命令就对了
read [-pt] 变量
选项:
-p:后面可以接提示字符
-t:后面可以接等待的【秒数】
使用案例
让使用者由键盘输入一内容,将该内容变成名为atest的变量
[root@localhost 15:46:33 ~]# read atest
This is atest # 光标会等待你输入,输入This is atest
[root@localhost 15:53:59 ~]# echo ${atest}
This is atest # 你刚刚输入的数据已经变成一个变量的内容
提示使用者30秒输入自己的名字,将输入字符作为名为named的变量内容
[root@localhost 15:54:09 ~]# read -p "please keyin your name: " -t 30 named
please keyin your name: csq # 这里是提示字符,输入csq
[root@localhost 15:57:05 ~]# echo ${named}
csq # 发现输入的数据又变成了一个变量的内容
declare,typeset
declare或typeset是一样的功能,就是声明变量的类型
。如果使用declare后面没有接任何参数,那么bash就会主动的将所有的变量名称与内容通通显示出来,就好像set一样。
declare [-aixr] 变量
选项:
-a: 将后面名为xxxx的变量定义成为数据(array)类型
-i:将后面名为xxxx的变量定义成为整数(integer)类型
-x:用法与export一样,就是将后面的变量变成环境变量
-r:将变量设置成为readonly类型,该变量不可被更改内容,也不能unset
使用案例
让变量sum进行100+300+50的求和结果
[root@localhost 16:15:28 ~]# sum=100+300+50
[root@localhost 16:15:38 ~]# echo ${sum}
100+300+50 # 为什么没有 帮我们求和,因为这个是文字形式的变量属性
[root@localhost 16:15:41 ~]# declare -i sum=100+50+300
[root@localhost 16:16:11 ~]# echo ${sum}
450
由于默认情况下,bash对于变量有几个基本定义:
- 变量类型默认为字符串,所以若不指定变量类型,则1+2为一个字符串而不是计算式。所以上述第一个执行的结果才会是那种情况
- bash环境中的数值运算,默认最多仅能到达整数形态,所以1/3结果为0
现在你晓得为什么你要进行变量声明了吧?如果需要非字符串类型的变量,那就得要进行变量的声明才行。
让sum变成环境变量
[root@localhost 16:28:45 ~]# export |grep sum
declare -ix sum="450" # 出现了,包括由i与x的定义
让sum变成只读属性,不可修改
[root@localhost 16:28:54 ~]# declare -r sum
[root@localhost 16:30:03 ~]# sum=111
-bash: sum: readonly variable # 不能改这个变量了!
让sum变成非环境变量的自定义变量吧
[root@localhost 16:30:16 ~]# declare +x sum # 将-变成+可以进行【取消】操作
[root@localhost 16:31:38 ~]# declare -p sum # -p可以单独列出变量的类型
declare -ir sum="450" # 只剩下,i,r的类型,不具有x
declare也是个很有用的功能,尤其是当我们需要使用到下面的数组功能时,它也可以帮我们声明数组的属性。数组在shell脚本中也是很常用的。如果你不小心将变量设置为【只读】,通常要注销在登录能恢复该变量的类型。
数组(array)变量类型
那么如何设置数组的变量与内容呢?
var[index]=content
意思说我有一个数组名为var,这个数组的内容为var[1]=小明,var[2]=小弔,var[3]=老明,那个index就是一些数字,重点是用中括号[]来设置。
[root@localhost 16:45:56 ~]# var[1]="small ming"
[root@localhost 17:09:46 ~]# var[2]="bro ming"
[root@localhost 17:10:12 ~]# var[3]="old ming"
[root@localhost 17:10:24 ~]# echo "${var[1]},${var[2]},${var[3]}"
small ming,bro ming,old ming
与文件系统及程序的限制关系
想象一个状况,文档Linux主机里面同时登录了10个人,这10个人不知道怎么高的,同时开启了100个文件,每个文件大小约为10MB,请问一下,Linux内存要多大才够他们使用?
[root@localhost ~]# declare -i sum=10*100*10 ; echo $sum
10000
10000MB=10GB,这样,内存完全不够他们用。为了预防这个情况的发生,所以我们的bash是可以限制用户的某些系统资源的,包括可以开启的文件数量,可以使用CPU时间,可以使用内存的总量
ulimit [-SHacdfltu] [配额]
选项:
-H:hard limit 严格的设置,必定不能超过这个设置的数值
-S:soft limit 警告的设置,可以超过这个设置值,超过则有警告信息
在设置上面,通常soft 会比hard小
-a:后面不接任何选项与参数,可列出所有的限制与额度。
-c:当某些程序发生错误时,系统可能会将该程序在内存中的信息写成文件(除错用)
这种文件就被称为内核文件。为此限制每个内核文件的最大容量
-f:此shell可以建立的最大文件容量(一般可能设置为2GB)单位为Kbytes
-d:程序可使用的最大段内存容量
-l:可用于锁定的内存量
-t:可使用的最大CPU时间(单位为秒)
-u:单一使用者可以使用的最大进程数量
使用案例
列出你目前身份(假设为一般账号)的所有限制数据数值
[csq@localhost ~]$ 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) 31116
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以下的容量文件
[csq@localhost ~]$ ulimit -f 10240
[csq@localhost ~]$ ulimit -a |grep 'file size'
core file size (blocks, -c) 0
file size (blocks, -f) 10240
[csq@localhost ~]$ dd if=/dev/zero of=10mb bs=1M count=15
文件大小超出限制
变量内容的删除、取代与替换
变量除了可以直接设置来修改原本的内容之外,有没有办法通过简单的操作来将变量的内容进行微调呢?
变量内容的删除与替换
先让小写的path自定义变量设置的与PATH内容相同
[csq@localhost ~]# path="$PATH"
[root@localhost ~]# echo ${path}
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/.local/bin:/root/bin
假设我不喜欢local/bin 这个目录,所以要将一个目录删掉,如何显示?
[root@localhost ~]# echo ${path#/*local/bin:}
/usr/sbin:/usr/bin:/root/.local/bin:/root/bin
删除前面所有的目录,仅保留最后一个目录
[root@localhost ~]# echo ${path##/*:}
/root/bin
因为在PATH这个变量内容中,每个目录都是以冒号【:】隔开的,所以要从头删掉目录就是介于斜线(/)到冒号(:)之间的数据。但是PATH中不止一个冒号(😃,所以#与##就分别代表:
- #:符合替换文字的【最短的】那一个
- ##:符合替换文字的【最长的】那一个
上面谈到的是从前面开始删除变量内容,那么如果想要从后面向前删除变量内容?这个时候就得使用百分比(%)符号了
我想要删除最后面那个目录,就是从:到bin为止的字符
[root@localhost ~]# echo ${path%:*bin}
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/.local/bin
# 注意最后面一个目录不见了
# 这个%符号代表由最后面开始向前删除
那如果我只想要保留第一个目录?
[root@localhost ~]# echo ${path%%:*bin}
/usr/local/sbin
假设你是csq,那你的MAIL变量应该是/var/spool/mail/csq,假设你只想保留最后面那个文件名,前面的目录名称都不要了,如何利用$MAIL变量来完成?(最长符合)
[root@localhost ~]# echo ${MAIL##/*/}
root
相反如果你只想拿掉文件名,保留目录名称,即【/var/spool/mail/】最短符合。
[root@localhost ~]# echo ${MAIL%/*}
/var/spool/mail
将path的变量内容内的sbin替换成大写的SBIN
[root@localhost ~]# echo ${path/sbin/SBIN}
/usr/local/SBIN:/usr/local/bin:/usr/sbin:/usr/bin:/root/.local/bin:/root/bin
# 这个部分就容易理解了。关键字在于那两个斜线,量斜线的中间的是旧字符
# 后面的是新字符,所以结果就会出现如上述的特殊字体不放
[root@localhost ~]# echo ${path//sbin/SBIN}
/usr/local/SBIN:/usr/local/bin:/usr/SBIN:/usr/bin:/root/.local/bin:/root/bin
# 如果是两条斜线,那么就变成所有符合的内容都会被替换。
变量设置方式 | 说明 |
---|---|
①KaTeX parse error: Expected '}', got '#' at position 4: {变量#̲关键词} <br />②{变量##关键词} | ①若变量内容从头开始的数据符合【关键词】,则将符合的最短数据删除 ②若变量内容从头开始的数据符合【关键词】,则将符合最长数据删除 |
①KaTeX parse error: Expected '}', got 'EOF' at end of input: {变量%关键词}<br />②{变量%%关键词} | ①若变量内容从尾向前的数据符合【关键词】,则将符合的最短数据删除 ②若变量内容从尾向前的数据符合【关键词】,则将符合的最长数据删除 |
① 变量 / 旧字符串 / 新字符串 < b r / > ② {变量/旧字符串/新字符串}<br />② 变量/旧字符串/新字符串<br/>②{变量//旧字符串/新字符串} | ①若变量内容符合【旧字符串】则【第一个旧字符串会被新字符串替换】 ②若变量内容符合【旧字符串】则【全部的旧字符串会被新字符串替换】 |
变量的测试与内容替换
测试以下是否存在username这个变量,若不存在则给予username内容为root
[root@localhost ~]# echo ${username}
# 出现空白,所以username不存在,也可能是空字符
[root@localhost ~]# username=${username-root}
[root@localhost ~]# echo ${username}
root # 因为username没有设置,所以主动给予名为root的内容
[root@localhost ~]# username="This is csqcsqcsq" # 主动设置username的内容
[root@localhost ~]# echo ${username}
This is csqcsqcsq # 因为username已经设置了
不过这还是有点问题。因为username可能已经被设置为【空字符串】了。果真如此的话,那你还可以使用下面的案例来给予username的内容为root
若username未设置或未空字符,则将username内容设置为root
[root@localhost ~]# username=""
[root@localhost ~]# username=${username-root}
[root@localhost ~]# echo ${username}
# 因为username被设置为空字符了,所以当然还是保留为空字符。
[root@localhost ~]# username=${username:-root}
[root@localhost ~]# echo ${username}
root # 加上【:】后若变量内容为空或是未设置,都能够以被后面的内容替换
大括号内有没有冒号【:】的差别还是很大的。加上冒号,被测试的变量未被设置或是已被设置未空字符串时,都能够用后面的内容来替换与设置。
下面例子中,那个var与str为变量,我们想要针对str是否有设置来决定var的值,一般来说,str:代表【str没设置或为空的字符串时】,至于str则仅为【没有该变量】
变量设置方式 | 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 |
根据上面这张表,我们来进行几个案例练习,首先来测试以下,如果旧变量(str)不存在时,我们要给予新变量一个内容,若旧变量存在则新变量内容以旧变量来替换,结果如下:
测试:假设str不存在(用unset)然后测试以下减号(-)的用法
[root@localhost ~]# unset str;var=${str-newvar}
[root@localhost ~]# echo "var=${var},str=${str}"
var=newvar,str= # 因为str不存在,所以var为newvar
测试若str已存在,测试一下var会变怎样?
[root@localhost ~]# str="oldvar";var=${str-newvar}
[root@localhost ~]# echo "var=${var},str=${str}"
var=oldvar,str=oldvar # 因为str存在,所以var等于str的内容
如果你想要将旧变量内容也一起替换掉的话,那么就使用等号(=)
[root@localhost ~]# unset str;var=${str=newvar}
[root@localhost ~]# echo "var=${var},str=${str}"
var=newvar,str=newvar # 因为str不存在,所以var str 均为newvar
测试如果str已经存在了,测试一下var会变怎样?
[root@localhost ~]# str="oldvar";var=${str=newvar}
[root@localhost ~]# echo "var=${var},str=${str}"
var=oldvar,str=oldvar # 因为str存在,所以var等于str的内容
那如果我只是向指定,如果旧变量不存在时,整个测试就告知我【有错误】,此时就能够使用问号【?】的帮忙
测试:若str不存在时,则var的测试结果直接显示“无此变量”
[root@localhost ~]# unset str;var=${str?}
-bash: str: 参数为空或未设置
测试:若str存在时,则var的内容会与str相同
[root@localhost ~]# str="oldvar";var=${str?novar}
[root@localhost ~]# echo "var=${var},str=${str}"
var=oldvar,str=oldvar # 因为str存在,所以var等于str的内容
基本上这种变量的测试也能通过shell脚本内的【if…then…】来处理,不过bash有提供这么简单的方式来测试变量,那我们也可以学一学。
旧变量内容也一起替换掉的话,那么就使用等号(=)
先假设str不存在用(unset),然后测试一下等号(=)的用法
[root@localhost ~]# unset str;var=${str=newvar}
[root@localhost ~]# echo "var=${var},str=${str}"
var=newvar,str=newvar # 因为str不存在,所以var str 均为newvar
测试如果str已经存在了,测试一下var会变怎样?
[root@localhost ~]# str="oldvar";var=${str=newvar}
[root@localhost ~]# echo "var=${var},str=${str}"
var=oldvar,str=oldvar # 因为str存在,所以var等于str的内容
那如果我只是向指定,如果旧变量不存在时,整个测试就告知我【有错误】,此时就能够使用问号【?】的帮忙
测试:若str不存在时,则var的测试结果直接显示“无此变量”
[root@localhost ~]# unset str;var=${str?}
-bash: str: 参数为空或未设置
测试:若str存在时,则var的内容会与str相同
[root@localhost ~]# str="oldvar";var=${str?novar}
[root@localhost ~]# echo "var=${var},str=${str}"
var=oldvar,str=oldvar # 因为str存在,所以var等于str的内容
基本上这种变量的测试也能通过shell脚本内的【if…then…】来处理,不过bash有提供这么简单的方式来测试变量,那我们也可以学一学。