v>

shell变量


bash Shell的变量

回到顶部
变量是bash非常重要的一个概念,我们将介绍重要的环境变量、变量的查看和设置等

什么是变量?

回到顶部
自定义普通变量只在当前bash环境下有效,并不会影响到bash创建的子进程。 而环境变量可以影响到bash的子进程。 某些特定变量会影响到bash的环境!比如我们之前多次提到过的$PATH环境变量! 我们在命令行下执行一下命令的时候,bash就是从$PATH环境变量所定义的目录顺序寻找命令的可执行文件。 如果有多个匹配的可执行文件,bash只会执行第一个找个的可执行文件。 如果找不到匹配的可执行文件,bash就会提示command not found。
常用的环境变量有PATH、HOME、MAIL、SHELL等,为了区别与自定义变量的不同,环境变量通常以大写字符来表示!
那么如何显示变量呢?这就需要使用到echo这个命令了!

变量的定义与显示

回到顶部
可以使用echo命令显示变量的值,引用变量的时候需要在变量名前面加上$符号,或者以${变量名}的形式。$符表示取出变量值的意思。 目前越多来多的是采用${变量名}的形式。
echo命令显示变量名的常用格式如下所示:
[peter@study ~]$ echo $variable
或
[peter@study ~]$ echo ${variable}
            
例如,显示PATH环境变量的值:
            [peter@study ~]$ echo $PATH
            /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
            [peter@study ~]$ echo ${PATH}
            /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
            
echo的功能还有很多, 这里只是显示变量值,更多echo的信息,请自行查看manpage:man echo。
我们知道如何查看一个变量的值了,但是如何定义一个变量呢?直接用等号(=)连接变量名与变量值即可! 例如定义一个变量myname,变量值为peter,我们先通过echo查看一下这个变量是否已经被定义过了:
              [peter@study ~]$ echo ${myname}

            
上面输出一个空行,说明这个变量还没有被定义!下面给变量myname设置变量值为peter:
[peter@study ~]$ myname=peter
再次打印变量的值:
[peter@study ~]$ echo ${myname}
peter
我们成功定义了一个变量myname,变量值为peter。在bash中,当一个变量名称尚未被设置时,默认的变量值就是空的。 这里要注意每个shell的语法都是不相同的,echo是bash的内置命令,在bash下echo一个没有定义的变量,显示的就是空行, 如果在其他shell环境下echo一个没有定义的变量,可能会出现错误信息!

echo命令可以用来回显字符串到标准输出,常用来显示变量的值,显示变量的时候,需要在变量前面加上$号。

回显字符串'Initroot is a website for learning Linux':

$ echo 'Initroot is a website for learning Linux'
显示环境变量HOME:
$ echo $HOME

执行效果如下:

                peter@peter-VirtualBox:~$ echo Initroot is a website for learning Linux
                Initroot is a website for learning Linux
                peter@peter-VirtualBox:~$ echo 'Initroot is a website for learning Linux'
                Initroot is a website for learning Linux
                peter@peter-VirtualBox:~$ echo $HOME
                /home/peter
              

echo命令用于在shell中打印shell变量的值,或者直接输出指定的字符串。 linux的echo命令,在shell编程中极为常用, 在终端下打印变量value的时候也是常常用到的,因此有必要了解下echo的用法。 echo命令的功能是在显示器上显示一段文字,一般起到一个提示的作用。

NAME
echo - display a line of text
SYNOPSIS
echo [SHORT-OPTION]... [STRING]...
echo LONG-OPTION
DESCRIPTION
Echo the STRING(s) to standard output.
-n do not output the trailing newline
-e enable interpretation of backslash escapes
-E disable interpretation of backslash escapes (default)
--help display this help and exit
--version output version information and exit
If -e is in effect, the following sequences are recognized:
\\ backslash
\a alert (BEL)
\b backspace
\c produce no further output
\e escape
\f form feed
\n new line
\r carriage return
\t horizontal tab
\v vertical tab
\0NNN byte with octal value NNN (1 to 3 digits)
\xHH byte with hexadecimal value HH (1 to 2 digits)
NOTE: your shell may have its own version of echo, which usually supersedes the version described here. Please refer to your shell's documentation for details about the options it supports.

bash变量的设置需要遵循一定的变量命名规则,这些规则如下所示:
变量的设置规则:
1. 变量名与变量值用等号=连接,例如myname=peter
2. 等号=两边以及变量名不能有空格符,例如myname = peter或myname=peter Tsai都是错误的变量设置方式;
3. 变量名由英文字母或数字组成,不能以数字开头,例如2myname=peter就是错误的变量名设置方式;
4. 变量值如果有空格符, 需要使用双引号"或单引号'将变量值括起来。双引号内的特殊字符如$, 还是保留原本的功能特性。而单引号内的特殊字符则为一般文本字符。
假如LANG的变量值为zh_TW.UTF-8,我们定义一个var变量:var="lang is $LANG", 那么通过echo显示该变量的值为lang is zh_TW.UTF-8, 如果将var变量值用单引号:var='lang is $LANG', 那么通过echo显示该变量的值就是lang is $LANG。
单引号与双引号的最大不同在于双引号仍然可以保持特殊符号的特殊功能,但单引号内的特殊字符会作为一般普通字符处理,并不会有特别的作用。
5. 可用转义字符\将特殊符号(如[Enter], $, \, 空格符, '等)变成一般字符,如:myname=peter\ Tsai;
6. 变量值可以通过其他命令的输出获得,带命令的变量值形式为:`命令`或 $(命令)。其中`为反单引号,在键盘上为数字键1左边那个按键。
例如我们通过uname -r命令可以获得linux内核的版本号:
                [peter@study ~]$ uname -r
                4.15.0-65-generic
              
将uname -r的输出作为变量值赋值给一个变量kernelversion:
              [peter@study ~]$ kernelversion=$(uname -r)
              
显示变量kernelversion的值:
              [peter@study ~]$ echo $kernelversion
4.15.0-65-generic
              
7. 可以对变量值进行累加,例如原变量为myname=peter,现在要将myname变量设置为peterpeng,只需要在原变量值的基础上追加peng即可。 原变量值可用"$变量名"或${变量名}两种形式。追加方式如下: myname="$myname"peng 或者 myname=${myname}peng。命令行示例如下:
                myname=peter
[peter@study ~]$ echo myname
myname
[peter@study ~]$ myname=peter
[peter@study ~]$ echo $myname
peter
[peter@study ~]$ myname="$myname"peng
[peter@study ~]$ echo $myname
peterpeng
[peter@study ~]$ myname=${myname}peng
[peter@study ~]$ echo $myname
peterpengpeng
              
上面在执行myname=${myname}peng之前,由于myname已经是peterpeng了,所有再使用${myname}peng形式追加的时候,变量值就变成peterpengpeng。
我们也可以给变量直接赋值一个新的变量值,例如上面直接给myname设置新值peterpeng:myname=peterpeng。
既然可以直接设置新值,为什么还要这么麻烦的追加呢?如果遇到变量值比较长,重新输入就会比较麻烦,所以更方便的做法就是在原来变量值的基础上追加就可以了, 比较常用的是给PATH环境变量追加新的目录。例如:PATH="$PATH":/home/bin或PATH=${PATH}:/home/bin
8. 用export将自定义普通变量变为环境变量:export PATH,环境变量可以影响bash的子进程,我们稍后讨论这种影响。
9. 通常大写字符为系统默认变量,自定义普通变量建议使用小写字符,方便判断;
10. 使用unser命令取消变量的定义:unset 变量名。
取消上面myname的定义:
                [peter@study ~]$ unset myname
peter@peter-VirtualBox:~$ echo $myname

                
定义变量name,变量值为peter
[peter@study ~]$ 1myname=peter
1myname=peter: command not found   #屏幕提示错误!因为不能以数字开头!
[peter@study ~]$ myname = peter
myname: command not found          #因为有空白!提示错误
[peter@study ~]$ name=peter
如果变量值为peter's name,变量值含有特殊字符,该如何输入呢?:
[peter@study ~]$ name=peter's name
> 
按下enter键后,bash给出了续行符,这是由于单引号与双引号必须成对出现,上面已经输入了一个',当你按下enter后, bash认为你还没有输入完,所以就会给出续行符等待用户继续输入。直到你输入另一个成对的单引号或者双引号,然后再按下enter后,才算结束。 当然这不是我们需要的。只需要按下ctrl-c结束即可!
[peter@study ~]$ name="peter's name"
用双引号括起来后,里面的单引号'就变成一般文本字符了,这样就可以成功定义了.
[peter@study ~]$ name='peter's name'
>
如果使用单引号',那么也会出现上面的问题,因为前两个单引号已成对,多了最后一个单引号'没有成对,bash还是认为用户没有输入完。
[peter@study ~]$ name=peter\'s\ name
上面使用转义字符反斜杠\转义特殊字符,例如单引号与空格键,这样也可以成功定义变量!
给PATH变量累加/home/peter/bin目录:
[peter@study ~]$ PATH=$PATH:/home/peter/bin
或
[peter@study ~]$ PATH="$PATH":/home/peter/bin
或
[peter@study ~]$ PATH=${PATH}:/home/peter/bin
上面三种形式都可以成功累加PATH环境变量。 当然并不推荐第一种形式,因为可能会出现问题,例如将myname变量值追加peng:
[peter@study ~]$ myname=$mynamepeng
上面相当于是将mynamepeng变量的变量值赋值给myname变量了。但是根本就不存在mynamepeng这个变量,所以就会出现问题了。 正确的做法是:
[peter@study ~]$ myname="$myname"peng
或
[peter@study ~]$ myname=${myname}peng
我们刚才提到可以将自定义普通变量转变为环境变量,不管是普通变量还是环境变量,本质上都是bash的变量。但是这两种变量又有一些区别, 主要的区别是自定义普通变量不可以被bash的子进程继承,而环境变量可以被bash的子进程继承。 换句话说,自定义普通变量只存在当前bash中,在bash的子进程中就不存在了。而环境变量在bash的子进程中依然有效。我们通过实例观察两者的区别:
[peter@study ~]$ myname=peter
[peter@study ~]$ echo $myname
peter
[peter@study ~]$ bash
[peter@study ~]$ echo $myname

上面我们定义了myname变量,通过echo打印变量的值为peter。然后进入另一个bash,此时相当于进入了第一个bash的子进程中了,再次用echo打印myname变量值,发现已经为空了。 说明自定义变量在bash的子进程中已经失效了。我们用exit命令退出bash子进程,回到刚才的bash,然后通过export命令将myname变为环境变量,重复执行刚才的操作,再次进入bash的子进程:
[peter@study ~]$ exit
[peter@study ~]$ export myname
[peter@study ~]$ echo $myname 
peter
[peter@study ~]$ bash
[peter@study ~]$ echo $myname
peter
将myname变量变为环境变量后,在bash的子进程中就可以显示该变量的值了,说明在bash的子进程中该变量值仍然是有效的。 相当于bash的子进程继承了父进程的环境变量。
退出当前bash,回到刚才的bash环境。
[peter@study ~]$ exit
进入当前系统内核模块目录:
[peter@study ~]$ cd /lib/modules/`uname -r`/kernel
或者
[peter@study ~]$ cd /lib/modules/$(uname -r)/kernel
推荐使用$(uname -r)的形式。每个linux发行版所用的linux内核版本可能都是不一样的, 所以也就会对应不同的内核模块目录。例如CentOS 7.1默认内核版本是3.10.0-229.el7.x86_64, 内核模块目录为/lib/modules/3.10.0-229.el7.x86_64/kernel/。 在编写shell脚本的时候,可能需要进入该目录。 如果在shell脚本中写cd /lib/modules/3.10.0-229.el7.x86_64/kernel/的话, 那么把这个脚本放到另外的linux发行版中可能就无法成功运行。因为内核版本号发生了变化,相应的内核模块目录也就发生变化了。
这时候就可以利用uname -r命令先取得内核版本信息。将该命令以变量的形式加入命令中。 这样就可以不需要更改shell脚本源代码在各个linux发行版中成功运行了。
在一串命令中,在反单引号`之内的命令将会被先执行,而其执行出来的结果将做为外部的输入信息!例如 uname -r 会显示出目前的核心版本,而我们的核心版本在 /lib/modules 里面,因此,你可以先执行 uname -r 找出核 心版本,然后再以 cd 目录到该目录下,当然也可以执行如同上面范例六的执行内容啰。
locate命令可以列出所有的相关文件档名,但是,如果我想要知道各个文件的权限呢?例如想知道每个crontab相关文件的权限:
[peter@study ~]$ ls -ld `locate crontab`
[peter@study ~]$ ls -ld $(locate crontab)
如此一来,先以 locate 将文件名数据都列出来,再以 ls 命令来处理的意思啦!瞭了吗? ^_^
取消变量myname的定义:
[peter@study ~]$ echo $myname
peter
[peter@study ~]$ unset myname
[peter@study ~]$ echo $myname

以上就是变量的设置和取消的内容了,比较重要的是一些特殊符号的使用,例如单引号'、双引号"、转义字符/、美元字符$、反单引号`等!
如果有一个常去的工作目录:/home/wwwroot/default/kblog/static/,如何进行该目录的简化?
在一般的情况下,如果你想要进入上述的目录得要cd /home/wwwroot/default/kblog/static/, 但如此一来变换目录就很麻烦。 此时就可以将目录定义为变量,如下所示:
[peter@study ~]$ work="/home/wwwroot/default/kblog/static/"
[peter@study ~]$ cd $work
上面我们在bash中定义的变量,即使没有用unser命令取消,在bash退出后也就自动消失了。这样在下次启动bash后,这些变量也就没有了。 那么如何才能保留这些变量的设置呢?只需要将这些变量的定义写入bash的配置文件~/.bashrc中即可。关于bash的配置文件我们后面介绍。

declare / typeset

回到顶部
declare 或 typeset 是一样的功能,就是在宣告变量的类型。如果使用 declare 后面并没有接任何参数, 那么 bash 就会主动的将所有的变量名称与内容通通叫出来,就好像使用 set 一样啦! declare常用语法格式如下所示:
[peter@study ~]$ declare [-aixr] variable
选项与参数:
-a :将variable变量定义为数组array类型;
-i :将variable变量定义为整型数值integer;
-x :用法与export命令一样,将variable变量变成环境变量;
-r :将变量设定成为只读类型readonly,该变量不可被更改内容,也不能unset;
让变量sum进行100+300+50的加总结果:
[peter@study ~]$ sum=100+300+50
[peter@study ~]$ echo ${sum}
100+300+50
怎么没有帮我计算加总?变量的默认类型为字符串类型.所以上面并没有做算数运算!
[peter@study ~]$ declare -i sum=100+300+50
[peter@study ~]$ echo ${sum}
450
在默认情况, bash变量类型默认为字符串.
bash中的数值运算,默认最多仅能到达整数形态,所以1/3结果是0;
将sum变成环境变量
[peter@study ~]$ declare -x sum
[peter@study ~]$ export | grep sum
declare -ix sum="450"
可以看到sum的定义有i和x!
让sum变成只读属性,不可更动!
[peter@study ~]$ declare -r sum
[peter@study ~]$ sum=tesgting
-bash: sum: readonly variable
这样就无法改变sum的变量值了.如果你不小心将变量设定为只读, 通常得要注销再登入才能复原该变量的类型了!
让sum恢复成非环境变量的自定义变量:
[peter@study ~]$ declare +x sum
[peter@study ~]$ declare -p sum
declare -ir sum="450"
上面我们使用+x取消环境变量属性,然后用-p观察变量的定义类型,只剩下i, r的类型,不具有x了!

数组 (array) 变量类型

回到顶部
某些时候,我们必须使用数组来宣告一些变量,这有什么好处啊?在一般人的使用上, 果然是看不 出来有什么好处的!不过,如果您曾经写过程序的话,那才会比较了解数组的意义~ 数组对写数值 程序的设计师来说,可是不能错过学习的重点之一哩!好!不啰唆~ 那么要如何设定数组的变量与 内容呢?在 bash 里头,数组的设定方式是:
var[index]=content
意思是说,我有一个数组名为 var ,而这个数组的内容为 var[1]=小明, var[2]=大明, var[3]=好明 .... 等等,那个 index 就是一些数字啦,重点是用中刮号 ([ ]) 来设定的。 目前我们 bash 提供的是一 维数组。老实说,如果您不必写一些复杂的程序, 那么这个数组的地方,可以先略过,等到有需要 再来学习即可!因为要制作出数组, 通常与循环或者其他判断式交互使用才有比较高的存在意义! 范例:设定上面提到的 var[1] ~ var[3] 的变数。
[peter@study ~]$ var[1]="small min"
[peter@study ~]$ var[2]="big min"
[peter@study ~]$ var[3]="nice min"
[peter@study ~]$ echo "${var[1]}, ${var[2]}, ${var[3]}"
small min, big min, nice min
数组的变量类型比较有趣的地方在于读取,一般来说,建议直接以 ${数组} 的方式来读取,比 较正确无误的啦!这也是为啥一开始就建议你使用 ${变量} 来记忆的原因!

变量的有效范围

回到顶部
这里所说的变量的有效范围其实就是指的普通自定义变量和环境变量的区别了.自定义变量的范围只作用于当前bash进程环境. 而环境变量的有效范围为当前bash及其子进程.自定义变量类似局部变量local variable,而环境变量类似全局变量global variable.
在学理方面,为什么环境变量的数据可以被子程序所引用呢?这是因为内存配置的关系!理论上是这 样的:
当启动一个 shell,操作系统会分配一记忆区块给 shell 使用,此内存内之变量可让子程序取用 若在父程序利用 export 功能,可以让自定义变量的内容写到上述的记忆区块当中(环境变量);
当加载另一个 shell 时 (亦即启动子程序,而离开原本的父程序了), 子 shell 可以将父 shell 的环境变量所在的记忆区块导入自己的环境变量区块当中。
不过要提醒的是,环境变量与bash的操作环境不是一回事儿,举例来说, PS1并不是环境变量, 但是这个PS1会影响到bash的接口环境提示字符

变量键盘读取: read,

回到顶部
read可以通过 我们上面定义变量的时候,直接在命令行输入"变量=变量值"即可. 我们经常会遇到在程序的执行过程中会要求用户输入相关的信息,例如最常见的是等待用户输入yes/no,这类程序称为交互式程序.
我们在编写shell script,也经常需要和用户交互,等待用户输入,将用户输入的信息存放在变量中.这时候就需要用到read命令了.
在命令行用"变量=变量值"定义变量的时候,变量默认的类型是字符串类型.如果我们想指定变量的类型,例如数组或者数值,就需要用到declare命令.
read读取来自键盘的输入,并将输入存入变量。read命令常用格式如下:
[peter@study ~]$ read [-pt] variable
选项与参数:
-p :后面可以接提示字符!
-t :后面接等待的秒数!
将用户输入作为atest变量的变量值:
[peter@study ~]$ read atest   
this is a read test             #输入enter后,光标会等待输入,输入本行内容后按enter
[peter@study ~]$ echo ${atest}
this is a read test
提示使用者 30 秒内输入自己的大名,将该输入字符串作为名为 named 的变量内容
[peter@study ~]$ read -p "Please keyin your name: " -t 30 named
Please keyin your name: peter peng   #注意看,会有提示字符喔!
[peter@study ~]$ echo ${named}
peter peng
read 之后不加任何参数,直接加上变量名称,那么底下就会主动出现一个空白行等待你的输入。 如果加上-t后面接秒数,指定的秒数之内没有任何动作时, 该命令就会自动略过了. 如果是加上-p,在输入的光标前就会有比较多可以用的提示字符给我们参考!多用于交互式shell

变量内容的删除、取代与替换 (Optional)

回到顶部
变量除了可以直接设定来修改原本的内容之外,有没有办法透过简单的动作来将变量的内容进行微调 呢? 举例来说,进行变量内容的删除、取代与替换等!是可以的!我们可以透过几个简单的小步骤 来进行变量内容的微调喔! 底下就来试试看!
变量内容的删除与取代变量的内容可以很简单的透过几个咚咚来进行删除喔!我们使用 PATH 这个变量的内容来做测试好 了。 请你依序进行底下的几个例子来玩玩,比较容易感受的到在这里想要表达的意义:
范例一:先让小写的 path 自定义变量设定的与 PATH 内容相同
[peter@study ~]$ path=${PATH}
[peter@study ~]$ echo ${path}
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/peter/.local/bin:/home/peter/bin
范例二:假设我不喜欢 local/bin,所以要将前 1 个目录删除掉,如何显示?
[peter@study ~]$ echo ${path#/*local/bin:}
/usr/bin:/usr/local/sbin:/usr/sbin:/home/peter/.local/bin:/home/peter/bin
上面这个范例很有趣的!他的重点可以用底下这张表格来说明:
${variable#/*local/bin:}
上面的特殊字体部分是关键词!用在这种删除模式所必须存在的
${variable#/*local/bin:}
这就是原本的变量名称,以上面范例二来说,这里就填写 path 这个变量名称啦!
${variable#/*local/bin:}
这是重点!代表从变量内容的最前面开始向右删除,且仅删除最短的那个
${variable#/*local/bin:}
代表要被删除的部分,由于 # 代表由前面开始删除,所以这里便由开始的 / 写起。 需要注意的是,我们还可以透过通配符 * 来取代 0 到无穷多个任意字符 以上面范例二的结果来看, path 这个变量被删除的内容如下所示:
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/peter/.local/bin:/home/peter/bin
很有趣吧!这样了解了 # 的功能了吗?接下来让我们来看看底下的范例三!
范例三:我想要删除前面所有的目录,仅保留最后一个目录
[peter@study ~]$ echo ${path#/*:}
/usr/bin:/usr/local/sbin:/usr/sbin:/home/peter/.local/bin:/home/peter/bin
# 由于一个 # 仅删除掉最短的那个,因此他删除的情况可以用底下的删除线来看:
# /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/peter/.local/bin:/home/peter/bin
[peter@study ~]$ echo ${path##/*:}
/home/peter/bin
# 嘿!多加了一个 # 变成 ## 之后,他变成删除掉最长的那个数据!亦即是:
# /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/peter/.local/bin:/home/peter/bin非常有趣! 不是吗?因为在 PATH 这个变量的内容中,每个目录都是以冒号:隔开的, 所以要 从头删除掉目录就是介于斜线 (/) 到冒号 (:) 之间的数据!但是 PATH 中不止一个冒号 (:) 啊! 所 以 # 与 ## 就分别代表:
# :符合取代文字的最短的那一个;
##:符合取代文字的最长的那一个
上面谈到的是从前面开始删除变量内容,那么如果想要从后面向前删除变量内容呢? 这个 时候就得使用百分比 (%) 符号了!来看看范例四怎么做吧!
范例四:我想要删除最后面那个目录,亦即从 : 到 bin 为止的字符串
[peter@study ~]$ echo ${path%:*bin}
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/peter/.local/bin
# 注意啊!最后面一个目录不见去!
# 这个 % 符号代表由最后面开始向前删除!所以上面得到的结果其实是来自如下:
# /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/peter/.local/bin:/home/peter/bin
范例五:那如果我只想要保留第一个目录呢?
[peter@study ~]$ echo ${path%%:*bin}
/usr/local/bin
# 同样的, %% 代表的则是最长的符合字符串,所以结果其实是来自如下:
# /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/peter/.local/bin:/home/peter/bin
由于我是想要由变量内容的后面向前面删除,而我这个变量内容最后面的结尾是/home/peter/bin,
所以你可以看到上面我删除的数据最终一定是bin,亦即是:*bin那个 * 代表通配符! 至于 %
与 %% 的意义其实与 # 及 ## 类似!这样理解否?
例题:
假设你是 peter ,那你的 MAIL 变量应该是 /var/spool/mail/peter 。假设你只想要保留最后面那个档名 (peter), 前面的目录名称都不要了,如何利用 $MAIL 变量来达成?
答:
题意其实是这样/var/spool/mail/peter,亦即删除掉两条斜线间的所有数据(最长符合)。 这个时候你就可 以这样做即可:
[peter@study ~]$ echo ${MAIL##/*/}
相反的,如果你只想要拿掉文件名,保留目录的名称,亦即是/var/spool/mail/peter (最短符合)。但假设 你并不知道结尾的字母为何,此时你可以利用通配符来处理即可,如下所示:
[peter@study ~]$ echo ${MAIL%/*}
了解了删除功能后,接下来谈谈取代吧!继续玩玩范例六啰!
范例六:将 path 的变量内容内的 sbin 取代成大写 SBIN:
[peter@study ~]$ echo ${path/sbin/SBIN}/usr/local/bin:/usr/bin:/usr/local/SBIN:/usr/sbin:/home/peter/.local/bin:/home/peter/bin
# 这个部分就容易理解的多了!关键词在于那两个斜线,两斜线中间的是旧字符串 # 后面的是新字符串,所以结果就会出现如上述的特殊字体部分啰!
[peter@study ~]$ echo ${path//sbin/SBIN}
/usr/local/bin:/usr/bin:/usr/local/SBIN:/usr/SBIN:/home/peter/.local/bin:/home/peter/bin
# 如果是两条斜线,那么就变成所有符合的内容都会被取代喔!
我们将这部份作个总结说明一下:

变量设定方式

回到顶部
说明
${变量#关键词}
${变量##关键词} 若变量内容从头开始的数据符合关键词,则将符合的最短数据删除
若变量内容从头开始的数据符合关键词,则将符合的最长数据删除
${变量%关键词}
${变量%%关键词} 若变量内容从尾向前的数据符合关键词,则将符合的最短数据删除
若变量内容从尾向前的数据符合关键词,则将符合的最长数据删除
${变量/旧字符串/新字符串}
${变量//旧字符串/新字符串} 若变量内容符合旧字符串则第一个旧字符串会被新字符串取代
若变量内容符合旧字符串则全部的旧字符串会被新字符串取代
变量的测试与内容替换
在某些时刻我们常常需要判断某个变量是否存在,若变量存在则使用既有的设定,若变量不存在 则给予一个常用的设定。 我们举底下的例子来说明好了,看看能不能较容易被你所理解呢!
范例一:测试一下是否存在 username 这个变量,若不存在则给予 username 内容为 root
[peter@study ~]$ echo ${username}
#由于出现空白,所以 username 可能不存在,也可能是空字符串
[peter@study ~]$ username=${username-root}
[peter@study ~]$ echo ${username}
root
#因为 username 没有设定,所以主动给予名为 root 的内容。
[peter@study ~]$ username="peter tsai" #主动设定 username 的内容
[peter@study ~]$ username=${username-root}
[peter@study ~]$ echo ${username}
peter tsai #因为 username 已经设定了,所以使用旧有的设定而不以 root 取代 在上面的范例中,重点在于减号 - 后面接的关键词!基本上你可以这样理解: new_var=${old_var-content}
新的变量,主要用来取代旧变量。新旧变量名称其实常常是一样的 new_var=${old_var-content}这是本范例中的关键词部分!必须要存在的哩!
new_var=${old_var-content}
旧的变量,被测试的项目!
new_var=${old_var-content}
变量的内容,在本范例中,这个部分是在给予未设定变量的内容
不过这还是有点问题!因为 username 可能已经被设定为空字符串了!果真如此的话,那你还可
以使用底下的范例来给予 username 的内容成为 root 喔!
范例二:若 username 未设定或为空字符串,则将 username 内容设定为 root
[peter@study ~]$ username=""
[peter@study ~]$ username=${username-root}
[peter@study ~]$ echo ${username}
#因为 username 被设定为空字符串了!所以当然还是保留为空字符串!
[peter@study ~]$ username=${username:-root}
[peter@study ~]$ echo ${username}
root
#加上 : 后若变量内容为空或者是未设定,都能够以后面的内容替换!
在大括号内有没有冒号 : 的差别是很大的!加上冒号后,被测试的变量未被设定或者是已被设 定为空字符串时, 都能够用后面的内容 (本例中是使用 root 为内容) 来替换与设定!这样可以了解 了吗?除了这样的测试之外, 还有其他的测试方法喔!将他整理如下:
底下的例子当中,那个 var 与 str 为变量,我们想要针对 str 是否有设定来决定 var Tips
的值喔! 一般来说, 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=$strvar=${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) ,然后测试一下减号 (-) 的用法:
[peter@study ~]$ unset str; var=${str-newvar}
[peter@study ~]$ echo "var=${var}, str=${str}"
var=newvar, str=
#因为 str 不存在,所以 var 为 newvar 测试:若 str 已存在,测试一下 var 会变怎样?:
[peter@study ~]$ str="oldvar"; var=${str-newvar}
[peter@study ~]$ echo "var=${var}, str=${str}"
var=oldvar, str=oldvar
#因为 str 存在,所以 var 等于 str 的内容 关于减号 (-) 其实上面我们谈过了!这里的测试只是要让你更加了解,这个减号的测试并不会影响到 旧变量的内容。 如果你想要将旧变量内容也一起替换掉的话,那么就使用等号 (=) 吧!
测试:先假设 str 不存在 (用 unset) ,然后测试一下等号 (=) 的用法:
[peter@study ~]$ unset str; var=${str=newvar}
[peter@study ~]$ echo "var=${var}, str=${str}"
var=newvar, str=newvar
#因为 str 不存在,所以 var/str 均为 newvar 测试:如果 str 已存在了,测试一下 var 会变怎样?
[peter@study ~]$ str="oldvar"; var=${str=newvar}
[peter@study ~]$ echo "var=${var}, str=${str}"
var=oldvar, str=oldvar
#因为 str 存在,所以 var 等于 str 的内容 那如果我只是想知道,如果旧变量不存在时,整个测试就告知我有错误,此时就能够使用问号 ?
的帮忙啦! 底下这个测试练习一下先!
测试:若 str 不存在时,则 var 的测试结果直接显示 "无此变量"
[peter@study ~]$ unset str; var=${str?无此变数}
-bash: str: 无此变量
#因为 str 不存在,所以输出错误讯息
测试:若 str 存在时,则 var 的内容会与 str 相同!
[peter@study ~]$ str="oldvar"; var=${str?novar}
[peter@study ~]$ echo "var=${var}, str=${str}"var=oldvar, str=oldvar
#因为 str 存在,所以 var 等于 str 的内容 基本上这种变数的测试也能够透过 shell script 内的 if...then... 来处理, 不过既然 bash 有提供这么 简单的方法来测试变量,那我们也可以多学一些嘛! 不过这种变量测试通常是在程序设计当中比较 容易出现,如果这里看不懂就先略过,未来有用到判断变量值时,再回来看看吧! ^_^

initroot编辑整理,转载请注明www.initroot.com

100次点赞 100次阅读