Shell | 变量知识进阶与实战 【详解】

Shell中特殊位置且重要的变量

 


在shell中存在一些特殊且重要的变量,例如:$0/$1/$#,我们称之为特殊位置参数变量。要从命令行、函数或脚本执行等处传递参数

时,就需要在shell脚本中使用位置参数变量。

以下 表格为常用的特殊位置参数变量的说明:


位置变量                                作用说明
$0获取当前执行的shell脚本的文件名,如果执行脚本包含了路径,那么就包括脚本路径

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

 

 

 

 

 

 

 

 

 

 

 

 

 

1.$1 $2...$9 ${10}${11}..特殊变量实践


编写如下的a.sh脚本,输入内容为"echo $1",并执行测试

[root@kang ~]# cat a.sh 
#!/bin/bash
echo $1
[root@kang ~]# sh a.sh blxh
blxh
[root@kang ~]# sh a.sh blxh girl
blxh
[root@kang ~]# sh a.sh "blxh girl"
blxh girl
[root@kang ~]#

解释:

[root@kang ~]# cat a.sh 
#!/bin/bash
echo $1 #<==脚本功能是打印脚本传递的第一个参数的值
[root@kang ~]# sh a.sh blxh #<==传入一个blxh字符串参数,赋值给脚本中的$1.
blxh <==把传入的blxh参数赋值给脚本中的$1并输出,因此输出结果为blxh。
[root@kang ~]# sh a.sh blxh girl <==传入两个字符串参数,但脚本不会接收第二个参数,参数默认是以空格分隔。
blxh <==只输出了blxh,因为脚本里没有加入$2,因此,无法接收第二个参数girl字符串。
[root@kang ~]# sh a.sh "blxh girl" <==加双引号括起来的内容传参,会作为一个字符串参数。
blxh girl <==虽然打印了,但是这些内容是作为一个参数传递给$1的。
[root@kang ~]# 

在脚本中同时加入$1和$2,并进行测试。

[root@kang ~]# sh a.sh chenglong bingbing #<--同时传入两个字符串参数。
chenglong
bingbing
[root@kang ~]# sh a.sh "chenglong bingbing" blxh #<==传入两个字符串参数,对第一个有空格的多个字符串用双引号引起来。
chenglong bingbing
blxh
[root@kang ~]# 


设置15个位置参数($1~$15),用于接收命令行传递的15个参数。

解释:

[root@kang ~]# echo \${1..15} <==利用大括号输出15个位置参数,学会了该命令就不用手敲代码了
$1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11 $12 $13 $14 $15
[root@kang ~]# echo \${1..15} >n.sh <==利用大括号输出15个位置参数并定向到文件n.sh里面
[root@kang ~]# cat n.sh 
$1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11 $12 $13 $14 $15 #<==增加echo命令打印所有参数,这是最终的测试代码,前面的都是为了写代码,读者也可以用vim编辑录入
[root@kang ~]# echo  {a..z} #<==测试打印26个字母a~z并以空格分隔
a b c d e f g h i j k l m n o p q r s t u v w x y z

以下是有关$1,$2,$3....这些位置参数的系统生产场景案例。对此,读者可以多参考rpcbind、NFS两个软件启动脚本,这两个服务的启动脚本简单、规范。若是最下化安装的系统,则表示没有安装rpcbind、NFS,可以通过执行以下命令去安装:

[root@kang ~]# yum -y install nfs-utils rpcbind

在生产场景中,执行如下命令后

/etc/init.d/rpcbind

rpcbind脚本后携带的start参数会传给脚本里的$1进行判断,脚本中传递参数的关键case语句节选如下:

case "$1" in #<==这里的$1用于接收执行此脚本命令行的第一个参数,规范用法是用双引号引起来。
   start) #<==如果$1接收的值匹配start,则执行下文的start函数及内部的指令。
       start #<==调用脚本中的start函数
       RETEVAL=$? #<==这里是记录start函数执行的返回值,$?也是重要的变量,暂时可以忽略,后面有介绍。
     ;;
   stop) #<==如果$1接收的值匹配stop,则执行下文的stop函数及内部的指令
      stop
      RETEVAL=$?
     ;;
   status)#<==如果$1接收的值匹配status,则执行下文的status函数及内部的指令。
      status $prog
      RETEVAL=$?
     ;;
   ...省略部分内容

2.$0特殊变量的作用及变量的实践


$0的作用为取出执行脚本的名称(包括路径),下面是该功能的实践

获取脚本的名称及路径:

如上图所示,若不带路径执行脚本,那么输出结果就是脚本的名字,如下:

解释:

[root@kang ~]# sh a.sh 
a.sh #<==$0获取的值就是脚本的名字,因此这里输出了a.sh
[root@kang ~]#

若使用全路径执行脚本,那么输出结果就是全路径加上脚本的名字,如下图所示:

解释:

[root@kang ~]# sh /root/a.sh 
/root/a.sh #<==如果执行的脚本中带有路径,那么$0获取的值就是脚本的名字加路径
[root@kang ~]# 

当要执行的脚本为全路径时,$0也会带着路径。此时如果希望单独获取名称或路径,则可用以下示例:

解释:

[root@kang ~]# dirname  /root/a.sh 
/root #<==dirname命令的作用是获取脚本的路径
[root@kang ~]# basename /root/a.sh 
a.sh  #<==basename命令的作用是获取脚本的名字
[root@kang ~]# 

提示:读者可以根据需求,用不同的命令获取对应的结果。


 利用$0和上述命令(dirname、basename)分别取出脚本名称和脚本路径

                                 

解释:

[root@kang ~]# sh /root/a.sh 
/root #<==这就是dirname $0的输出结果
a.sh  #<==这就是basename $0的输出结果
[root@kang ~]# 

有关$0这个位置参数的系统生产场景案例如下,其中采用的是network系统脚本。

解释:

[root@kang ~]# tail -6 /etc/init.d/network 
*)
    echo $"Usage: $0 {start|stop|status|restart|force-reload}" #<==$0的基本生产场景就是,当用户输入不符合脚本的要求时,就打印脚本的名字及使用帮助
    exit 2
esac

exit $rc
[root@kang ~]# /etc/init.d/network #<==不带任何参数执行network脚本
Usage: /etc/init.d/network {start|stop|status|restart|force-reload} #<==/etc/init.d/network就是$0从脚本命令行获取的值,当用户输入不符合脚本设定的要求时,打印脚本名字及预期的使用帮助
[root@kang ~]# 

3.$#特殊变量获取脚本传参个数的实践


通过$#获取脚本传参的个数

解释:

[root@kang ~]# cat a.sh 
#!/bin/bash
echo $1 $2 $3 $4 $5 $6 $7 $8 $9 
echo $# #<==此行是打印脚本命令行传参的个数
[root@kang ~]# sh a.sh  {a..z} #<==传入26个字符作为26个参数
a b c d e f g h i  #<==只接收了9个变量,所以打印9个字符
26 #<==传入26个字符作为26个参数,因此这里的数字为26,说明传入了26个参数
[root@kang ~]#

根据用户在命令行的传参个数判断用户的输入,不合要求的给予提示并退出。

这是一个针对$0、$1、$#等多位置参数的综合型企业案例。

首先来看看条件表达式判断语句的写法,如下:

执行结果:

解释:

[root@kang ~]# sh a.sh arg1 
muse two args #<==如果传入的参数不够,即不符合要求,则直接给出提示
[root@kang ~]# sh a.sh arg1 arg2
blxh  #<==当参数满足要求后,打印blxh的字符串
[root@kang ~]# 

然后是if判断语句的写法,如下:

实现效果如下:

解释:

[root@kang ~]# sh a.sh arg1 #<==若传参的个数不够两位,则直接给出提示。
USAGE:/bin/sh a.sh arg1 arg2 #<==a.sh就是脚本中$0获取的值
[root@kang ~]# sh a.sh arg1 arg2
arg1 arg2  #<==若参数满足要求,则打印$1和$2获取的字符串,即arg1和arg2
[root@kang ~]# 

4.$*和$@特别变量功能及区别说明

上面的表格已经列举出了$*和$@的区别以及作用,再来看看示例如下:

利用set设置位置参数(同命令行脚本的传参)

测试$*和$@,注意,此时不带双引号:

解释:

[root@kang ~]# echo $*
I am handsome blxh.
[root@kang ~]# echo $@
I am handsome blxh.
[root@kang ~]# for i in $*;do echo $i;done
I
am
handsome
blxh.
[root@kang ~]# for i in $@;do echo $i;done #<==使用for循环输出$*测试。
I #<==($*)不加双引号,因此会输出所有参数,然后第一个参数"I am"也拆开输出了
am
handsome
blxh.
[root@kang ~]#

测试"$*"和"$@",注意,此时带有双引号:

解释:

[root@kang ~]# echo "$*"
I am handsome blxh.
[root@kang ~]# echo "$@"
I am handsome blxh.
[root@kang ~]# for i in "$*";do echo $i;done #<==在有双引号的情况下"$*",参数里引号中的内容当作一个参数输出了!
I am handsome blxh.
[root@kang ~]# for i in "$@";do echo $i;done #<==在有双引号的情况下,每个参数均以独立的内容输出。
I am #<==有双引号算一个参数
handsome
blxh. #<==这才真正符合我们传入的参数需求,set -- "I am" handsome blxh.
[root@kang ~]# for i;do echo $i;done  #<==去掉in 变量列表,相当于有引号的in "$@"。
I am
handsome
blxh.  #<==这才真正符合我们传入的参数需求,set -- "I am" handsome blxh.
[root@kang ~]# for i in $*;do echo $i;done #<==($*)不加双引号,因此会输出所有参数,然后第一个参数"I am"也拆开输出了。
am
handsome
blxh. 
[root@kang ~]# shift #<==用shift将位置参数移位(左移)
[root@kang ~]# echo $#
2
[root@kang ~]# echo $1 #<==这里就打印原来$2的值了
handsome
[root@kang ~]# echo $2 #<==这里就打印原来$3的值了
blxh.

提示:有关set和eval的使用可以查看man帮助手册

shell进程中的特殊状态变量


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

 

 

 

 

 

 

 

 

提示:查找上述知识的方法为使用man bash命令,然后搜索关键字“Special Parameters”。


1.$?特殊变量功能实践

执行命令后获取返回值(切换到kangshuo用户进行测试)。

解释:

[root@kang ~]# pwd #<==执行pwd命令,然后用echo $?查看执行命令的状态返回值。
/root
[root@kang ~]# echo $? 
0 #<==返回0,表示上一个命令的执行是成功的
[root@kang ~]# su kangshuo
[kangshuo@kang root]$ ls /root #<==列表root目录的内容
ls: 无法打开目录/root: 权限不够  
[kangshuo@kang root]$ echo $?
2 #<==返回值为非0,表示上一个命令(ls /root)执行错误。注意:对于不同的错误,返回值是不同的。
[kangshuo@kang root]$ rm -rf /root #<==删除/root目录及其子目录
rm: 无法删除"/root": 权限不够
[kangshuo@kang root]$ echo $?
1 #<==返回值为1(非0)。
[kangshuo@kang root]$ blxh  #<==执行一个不存在的命令
bash: blxh: 未找到命令...
[kangshuo@kang root]$ echo $?
127 #<==返回值为127(非0)。
[kangshuo@kang root]$ 




不同命令的执行结果中,$?的返回值不尽相同,但在工作场景中,常用的就是0和非0两种状态,0表示成功运行,非0表示运行失败。

根据返回值来判断软件的安装是否成功。例如简单安装Apache服务如下:

在企业场景下,$?返回值的用法如下:

判断命令、脚本或函数等程序是否执行成功。

若在脚本中调用执行exit数字,则会返回这个数字给$?变量

如果是在函数里,则通过return数字把这个数字以函数返回值的形式传给$?


2.$$特殊变量功能及实践

获取脚本执行的进程号(PID)

解释:

[root@kang ~]# cat a.sh #<==编写一个简单的脚本
#!/bin/bash
echo $$ >/tmp/a.pid  #<==获取$$的值,并重定向到/tmp/a.pid里。
sleep 300 #<==休息300秒,模拟守护进程不退出
[root@kang ~]# ps -ef | grep a.sh | grep -v grep
[root@kang ~]# sh a.sh &  #<==在后台运行脚本,&符号表示在后台运行。
[1] 9139 #<==这是脚本的进程号。
[root@kang ~]# ps -ef | grep a.sh | grep -v grep
root       9139   8777  0 14:30 pts/0    00:00:00 sh a.sh #<==这是脚本的进程号
[root@kang ~]# cat /tmp/a.pid 
9139  #<==这是$$对应的值。
[root@kang ~]#

提示:到这里大家应该明白了吧,$$就是获取当前执行的Shell脚本的进程号。

实现系统中多次执行某一个脚本后的进程只有一个(此为$$的企业级应用)

说明:有时执行定时任务脚本的频率比较快,并不知道上一个脚本是否真的执行完毕,但是,业务要求同一时刻只能有一个同样的脚本在运行,此时就可以利用$$获取上一次运行的脚本进程号,当程序重新运行时,根据获得的进程号,清理掉上一次的进程,运行新的脚本命令,脚本如下:

执行结果如下:

解释:

[root@kang ~]# ps -ef | grep pid.sh | grep -v grep
[root@kang ~]# sh pid.sh & #<==后台运行脚本
[1] 9400
[root@kang ~]# ps -ef | grep pid.sh | grep -v grep #<==查看启动的脚本进程
root       9400   8777  0 14:50 pts/0    00:00:00 sh pid.sh #<==只有一个进程
[root@kang ~]# sh pid.sh & #<==多次运行脚本,每次都会将上一次运行的杀掉
[2] 9415
[root@kang ~]# sh pid.sh & #<==多次运行脚本,每次都会将上一次运行的杀掉
[3] 9419
[1]   已终止               sh pid.sh
[root@kang ~]# sh pid.sh &
[4] 9423
[2]   已终止               sh pid.sh
[root@kang ~]# ps -ef | grep pid.sh | grep -v grep
root       9423   8777  0 14:51 pts/0    00:00:00 sh pid.sh  #<==发现无论运行多少次脚本,都只有一个进程。
[3]-  已终止               sh pid.sh
[root@kang ~]# 

提示:这是一个生产案例的简单模拟,脚本用于执行启动或定时任务时,相同的脚本中只能有一个在运行,当新脚本运行时,必须关闭未运行完或未退出的上一次的同名脚本进程。

3.$_特殊变量功能说明及实践

$_的作用是获得上一条命令的最后一个参数值,此功能用得不多,了解即可。

解释:

[root@kang ~]# /etc/init.d/network start blxh
Starting network (via systemctl):                          [  确定  ]
[root@kang ~]# echo $_  #<==打印上一条命令的最后一个参数值,即blxh
blxh
[root@kang ~]# 

4.$!特殊变量功能说明及实践

$!的功能类似于$$,只不过作用是获取上一次执行脚本的pid,对此,了解即可。

解释:

[root@kang ~]# ps -ef | grep pid.sh | grep -v grep
[root@kang ~]# sh pid.sh &  #<==后台运行pid.sh脚本
[1] 13716
[root@kang ~]# echo $! #<==获取前一次执行脚本pid.sh的进程号
13716
[root@kang ~]# ps -ef | grep pid.sh | grep -v grep
root      13716  13572  0 21:26 pts/1    00:00:00 sh pid.sh
[root@kang ~]#

bash Shell内置变量命令

bash shell 包含一些内置命令。这些内置命令在目录列表里是看不见的,它们由shell本身提供。常用的内部命令有:echo、eval、exec、export、read、shift,下面简单介绍几个最常用的内置命令的格式和功能。


(1.)echo在屏幕上输出信息

命令格式:echo args #<==可以是字符串和变量的组合

功能说明:将echo命令后面args指定的字符串及变量等显示到标准输出

echo参数选项说   明
-n不换行输出内容
-e解析转义字符(见下面的字符)
转义字符: 
\n

换行

\r回车
\t制表符(tab)
\b退格
\v纵向制表符

 

 

 

 

 

 

 

 

 


echo示例如下:

解释:

[root@kang ~]# echo -e
"blxh\tchenglong\tzhangmanyu\nlinqingxia\twangzuxian\tqiushuzhen\t" #<==加上-e解析以\开头的字符。
blxh    chenglong       zhangmanyu
linqingxia      wangzuxian      qiushuzhen
[root@kang ~]# echo blxh;echo girl
blxh
girl
[root@kang ~]# echo -n blxh;echo girl #<==不换行输出
blxhgirl
[root@kang ~]# echo "blxh\tzhouxingchi\ngirl\tzhangxueyou"
blxh\tzhouxingchi\ngirl\tzhangxueyou
[root@kang ~]# printf "blxh\tgirl\nblxh\tgirl\n" #<==printf的转义字符能力与echo类似。
blxh    girl
blxh    girl
[root@kang ~]# echo -e "1\b23" #<==加上-e解析以\开头的字符,\b退格。
23
[root@kang ~]# printf "1\b23\n" #<==printf的转义字符功能与echo类似。
23
[root@kang ~]# 



提示:printf与echo的功能类似,但是printf更强大,当需要特殊复杂的格式时才考虑使用printf。


(2.)eval

命令格式:eval args

功能:当shell程序执行到eval语句时,shell读入参数args,并将它们组合成一个新的命令,然后执行。

set和eval命令的使用 (含特殊位置变量用法)方法。

解释:

[root@kang ~]# cat noeval.sh 
echo \$$# #<==$#表示传参的个数。

[root@kang ~]# sh noeval.sh arg1 arg2 #<==传入两个参数。
$2 #<==传入两个参数,因此$#为2,于是echo \$$#就变成了echo $2,最后输出$2,并没有打印arg2.
[root@kang ~]# cat eval.sh 
#!/bin/bash
eval "echo \$$# "  #<==加上eval命令,使得打印的特殊位置参数,重新解析输出,而不是输出$2本身。
[root@kang ~]# sh eval.sh arg1 arg2
arg2 #<==输出了$2.
[root@kang ~]# 

(3.)exec

命令格式:exec命令参数

功能:exec命令能够在不创建新的子进程的前提下,转去执行指定的命令,当指定的命令执行完毕后,该进程(也就是最初的Shell)就终止了。

示例如下:

当使用exec打开文件后,read命令每次都会将文件指针移动到文件的下一行进行读取,直到文件末尾,利用这个可以实现处理文件内容。

exec的功能示例如下:

执行结果如下:

解释:

[root@kang ~]# seq 5 >/tmp/tmp.log 
[root@kang ~]# cat exe.sh 
#!/bin/bash
exec < /tmp/tmp.log #<==读取log内容
while read line     #<==利用read一行行读取处理
do
  echo $line        #<==打印输出
done
echo ok

(4.)read

命令格式:read  变量名表

功能:从标准输入读取字符串等信息,传给Shell程序内部定义的变量。

例如:

(5.)shift

命令格式:shift-Shift positional parameters

功能:shift语句会按如下方式重新命名所有的位置参数变量,即$2成为$1、$3成为$2等,以此类推,在程序中每使用一次shift语句,都会使所有的位置参数依次向左移动一个位置,并使位置参数$#减1,直到减到0为止。

shift命令的使用示例如下:

执行效果如下:

解释:

[root@kang ~]# cat n.sh 
#!/bin/bash
echo $1 $2
if [ $# -eq 2 ];then
   shift 
   echo $1
fi

[root@kang ~]# sh n.sh  1 2
1 2  #<==这是echo $1 $2 的结果。
2    #<==这里是echo $1的结果,但是输出的是传参时$2的值。

当我们写Shell希望像命令行的命令通过参数控制不同的功能时,就会先传一个类似-c的参数,然后再接内容。

解释:

[root@kang ~]# sh n.sh -c blxh
-c blxh  #<==对应$1 $2 的输出。
blxh   #<==对应$1的输出,因为执行了shift,因此第二个参数$2的内容,就变成了$1,所以输出了blxh。
[root@kang ~]# 

(6.)exit

命令格式:exit-Exit the shell

功能:退出shell程序。在exit之后可以有选择地指定一个数位作为返回状态。


Shell变量子串知识及实践


Shell变量子串的常用操作如下表:

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

使用string代替所有匹配的pattern

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 定义BLXH的变量,赋值内容为“I am  blxh”,操作代码如下:

 解释:

[root@kang ~]# BLXH="I am blxh" #<==这里的BLXH变量,就是上表中parameter变量的具体示例
[root@kang ~]# echo ${BLXH}  #<==带大括号打印变量BLXH
I am blxh
[root@kang ~]# echo $BLXH   #<==直接打印变量BLXH
I am blxh
[root@kang ~]# 

返回BLXH变量值的长度

通过在变量名前加#,就可以打印变量值的长度:

解释:

[root@kang ~]# echo ${#BLXH}
9  #<==I am blxh.这些字符加起来正好是9
[root@kang ~]#

Shell的其他打印变量长度的方法,代码如下:

解释:

[root@kang ~]# echo $BLXH | wc -L #<==输出变量值,然后通过管道交给wc计算长度。
9
[root@kang ~]# expr length "$BLXH"  #<==利用expr的length函数计算变量长度。
9
[root@kang ~]# echo "$BLXH" | awk '{print length($0)}' #<==利用awk的length函数计算变量长度,也可无“($0)”这几个字符。
9
[root@kang ~]# 

提示:上述计算变量长度的方法中,变量的字符串方式是最快的,即${#BLXH}的方式。

利用time命令及for循环对几种获取字符串长度的方法进行性能比较。

(1.)变量自带的获取长度的方法(echo ${#char})

解释:

[root@kang ~]# time for n in {1..10000};do char=`seq -s "blxh" 100`;echo ${#char} &>/dev/null;done

real    0m17.025s #<==变量自带的获取长度的方法用时最少,效率最高。
user    0m9.333s
sys     0m7.589s
[root@kang ~]# 

(2.)利用管道加wc的方法(echo${char}|wc -L)

解释:

[root@kang ~]# time for n in {1..10000};do char=`seq -s "blxh" 100`;echo ${char}|wc -L &>/dev/null;done

real    0m57.814s #<==使用了管道加wc -L计算,结果倒数第二,仅次于管道加awk统计的。
user    0m31.108s
sys     0m26.424s
[root@kang ~]# 

(3.)利用expr自带的length方法(expr length "${char}")

解释:

[root@kang ~]# time for n in {1..10000};do char=`seq -s "blxh" 100`;expr length "${char}" &>/dev/null;done   
real    0m35.742s  #<==好于使用管道和wc的计算方法,但是比变量自带的获取长度方法要差一些。
user    0m12.893s
sys     0m22.637s
[root@kang ~]# 

(4.)利用awk自带的length函数方法

解释:

[root@kang ~]# time for n in {1..10000};do char=`seq -s "blxh" 100`;echo $char | awk '{print length($0)}' &>/dev/null;done

real    1m4.938s  #<==使用了管道还有awk的函数计算,结果最差。
user    0m27.496s
sys     0m37.082s
[root@kang ~]#

可以看到,这几种方法的速度相差几十到上百倍,一般情况下调用外部命令来处理的方式与使用内置操作的速度相差较大。在shell编程中,应尽量使用内置操作或函数来完成。


截取BLXH变量的内容,从第2个字符之后开始截取,默认截取后面字符的全部,第2个字符不包含在内,也可以理解为删除前面的多个字符。

解释:

[root@kang ~]# echo ${BLXH}
I am blxh
[root@kang ~]# echo ${BLXH:2}
am blxh #<==相当于从I后面的空格开始计算,截取到了结尾。
[root@kang ~]# 

截取BLXH变量的内容,从第2个字符之后开始截取,截取2个字符。

解释:

[root@kang ~]# echo ${BLXH:2:2}
am
提示:这个功能类似于cut命令-c参数的功能
[root@kang ~]# echo ${BLXH}|cut -c 3-4 #<==输出变量的内容,管道交给cut截取第3~4个位置的字符。
am
[root@kang ~]# 

从变量$BLXH内容的开头开始删除最短匹配“a*C”及“a*c”的子串。

解释:

[root@kang ~]# BLXH=abcABC123ABCabc
[root@kang ~]# echo $BLXH
abcABC123ABCabc 
[root@kang ~]# echo ${BLXH#a*C} #<==从开头开始删除最短匹配“a*C”的子串
123ABCabc  #<==从开头开始删除了abcABC
[root@kang ~]# echo ${BLXH#a*c}  #<==从开头开始删除最短匹配“a*c”的子串
ABC123ABCabc #<==从开头开始删除了abc
[root@kang ~]# 

从变量$BLXH开头开始删除最长匹配“a*C”及“a*c”的子串。

解释:

[root@kang ~]# BLXH=abcABC123ABCabc
[root@kang ~]# echo $BLXH          
abcABC123ABCabc
[root@kang ~]# echo ${BLXH##a*c} #<==从开头开始删除最长匹配“a*c”的子串
                    #<==结果为空了,说明都匹配了,全部删除了。
[root@kang ~]# echo ${BLXH##a*C} #<==从开头开始删除最长匹配“a*C”的子串
abc   #<==结果为abc,说明匹配了ABCABC123ABC并删除了这些字符。
[root@kang ~]# 

从变量$BLXH结尾开始删除最短匹配“a*C”及“a*c”的子串

解释:

[root@kang ~]# BLXH=abcABC123ABCabc
[root@kang ~]# echo $BLXH          
abcABC123ABCabc
[root@kang ~]# echo ${BLXH%a*C} #<==从结尾开始删除最短匹配“a*C”的子串。
abcABC123ABCabc #<==原样输出,因为从结尾开始a*C没有匹配上任何子串,因此,没有删除任何字符。
[root@kang ~]# echo ${BLXH%a*c} #<==从结尾开始删除最短匹配“a*c”的子串。
abcABC123ABC  #<==从结尾开始删除最短匹配“a*c”,即删除了结尾的abc三个字符。
[root@kang ~]# 

从变量$BLXH结尾开始删除最长匹配“a*C”及“a*c”的子串。

解释:

[root@kang ~]# BLXH=abcABC123ABCabc
[root@kang ~]# echo $BLXH
abcABC123ABCabc
[root@kang ~]# echo ${BLXH%%a*C} #<==从结尾开始删除最长匹配“a*C”的子串。
abcABC123ABCabc #<==原样输出,因为从结尾开始“a*C”没有匹配上任何子串,因此,没有删除任何字符。
[root@kang ~]# echo ${BLXH%%a*c} #<==从结尾开始删除最长匹配“a*c”的子串。
              #<==从结尾开始删除最长匹配“a*c”的字符串,即删除全部字符。
[root@kang ~]# 



  • 有关上述匹配删除的小结:
  • #:表示从开头删除匹配最短
  • ##:表示从开头删除匹配最长
  • %:表示从结尾删除匹配最短
  • %%:表示从结尾删除匹配最长
  • *:表示匹配所有

使用girl字符串代替变量$BLXH匹配的blxh字符串。

解释:

[root@kang ~]# BLXH="I am blxh,yes,blxh"
[root@kang ~]# echo $BLXH
I am blxh,yes,blxh
[root@kang ~]# echo ${BLXH/blxh/girl} #<==替换匹配的第一个字符串。
I am girl,yes,blxh #<==真的只替换了第一个。
[root@kang ~]# echo ${BLXH//blxh/girl} #<==替换匹配的所有字符串。
I am girl,yes,girl  #<==真的替换了所有匹配blxh的字符串。
[root@kang ~]# 


提示:

一个“/“表示替换匹配的第一个字符串。

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


变量子串的生产场景应用案例:

去掉下面所有文件的文件名中的“_finished”字符串。

要想批量改名,首先得知道单个的文件应如何改名。单个文件的改名命令为:

[root@kang ~]# mv stu_102999_1_finished.jpg stu_102999_1.jpg

下面利用变量赋值和替换的方式实现上述mv命令的改名要求。

解释:

[root@kang ~]# f=stu_102999_1_finished.jpg #<==取一个文件名赋值给变量f,这个就是要修改的源文件
[root@kang ~]# echo ${f//_finished/} #<==利用变量的子串替换功能把变量f里的_finished替换为空。
stu_102999_1.jpg
[root@kang ~]# mv $f `echo ${f//_finished/}` #<==使用mv命令执行修改操作,注意目标命令要用反引号括起来。
[root@kang ~]# ls -l *.jpg
-rw-------. 1 root root 51 3月  17 12:06 stu_102999_1.jpg  #<==改过后的结果
-rw-------. 1 root root 51 4月  10 10:01 stu_102999_2_finished.jpg
-rw-------. 1 root root 51 4月  10 10:01 stu_102999_3_finished.jpg
-rw-------. 1 root root 51 4月  10 10:01 stu_102999_4_finished.jpg
-rw-------. 1 root root 51 4月  10 10:01 stu_102999_5_finished.jpg
[root@kang ~]# 

学会处理了一个,就可以进行批量处理了,批量处理就是利用循环而已。

[root@kang ~]# for f in `ls *fin*.jpg`;do mv $f `echo ${f//_finished/}`;done

解释:

[root@kang ~]# for f in `ls *fin*.jpg`;do mv $f `echo ${f//_finished/}`;done  
#<==其实就是使用for循环,循环上面是进行单个处理的mv命令。mv $f `echo ${f//_finished/}`
[root@kang ~]# ls -l *.jpg
-rw-------. 1 root root 51 3月  17 12:06 stu_102999_1.jpg
-rw-------. 1 root root 51 4月  10 10:01 stu_102999_2.jpg
-rw-------. 1 root root 51 4月  10 10:01 stu_102999_3.jpg
-rw-------. 1 root root 51 4月  10 10:01 stu_102999_4.jpg
-rw-------. 1 root root 51 4月  10 10:01 stu_102999_5.jpg
[root@kang ~]# 

Shell特殊扩展变量的知识与实践


shell的特殊扩展变量说明见下表,读者可执行man bash命令,然后搜索“Parameter Expansion”查找相关的帮助内容。

表达式说明
${parameter:-word}

如果parameter的变量值为空或未赋值,则会返回Word字符串并替代变量的值。用途:如果变量未定义,则返回备用的值,防止变量为空值或因未定义而导致异常

${parameter:=word}如果parameter的变量值为空或未赋值,则设置这个变量值为Word,并返回其值。位置变量和特殊变量不适用。用途:用于捕捉由于变量未定义而导致的错误,并退出程序。
${parameter:?word}

如果parameter变量值为空或未赋值,那么Word字符串将被作为标准错误输出,否则输出变量的值。

用途:用于捕捉由于变量未定义而导致的错误,并退出程序。

${parameter:+Word}如果parameter变量值为空或未赋值,则什么都不做,否则Word字符串将替代变量的值

 

 

 

 

 

 

 

 

 

 

 


 提示:每个表达式内的冒号都是可选的,如果省略了表达式中的冒号,则将每个定义中的“为空或未赋值”部分改为“未赋值”,也就是说,运算符仅用于测试变量是否未赋值。更多内容,请执行man bash命令查看帮助。


Shell特殊扩展变量的实践


1.${parameter:-word}功能实践

提示:${parameter:-word}的作用是如果parameter变量值为空或未赋值,则会返回Word字符串替代变量的值。

${parameter:-word}用法功能示例:

解释:

[root@kang ~]# echo $test #<==变量未设置,所以输出时为空。

[root@kang ~]# result=${test:-UNSET} #<==若test没值,则返回UNSET
[root@kang ~]# echo $result #<==打印result变量,返回UNSET,因为test没有赋值。
UNSET
[root@kang ~]# echo ${test} #<==注意,此时打印test变量还是为空

[root@kang ~]# 

结论:对于${test:-UNSET},当test变量没值时,就返回变量结尾设置的UNSET字符串。

解释:

[root@kang ~]# test=blxh #<==给test变量赋值blxh字符串
[root@kang ~]# echo $test
blxh
[root@kang ~]# result=${test:-UNSET}  #<==重新定义result
[root@kang ~]# echo $result
blxh  #<==因为test已经赋值,因此,打印result就输出了test的值blxh,而不是原来的UNSET
提示:这个变量的功能可以用来判断变量是否已定义
[root@kang ~]# result=${test-UNSET} #<==定义时忽略了冒号
[root@kang ~]# echo $result
blxh #<==打印结果和带冒号时没有变化
[root@kang ~]# 

结论:当test变量有值时,就打印result变量,返回test变量的内容。

2.${parameter:=word}功能实践

${parameter:=word}的作用是:如果parameter变量值为空或未赋值,就设置这个变量值为Word,并返回其值。位置变量和特殊变量不适用。

${parameter:=word}用法功能示例

 

解释:

[root@kang ~]# unset result #<==撤销result变量定义
[root@kang ~]# echo $result

[root@kang ~]# unset test  #<==撤销test变量定义
[root@kang ~]# echo $test

[root@kang ~]# result=${test:=UNSET} #<==重新对变量result进行定义
[root@kang ~]# echo $result
UNSET
[root@kang ~]# echo $test  #<==注意,这里的test原来是没有定义的,现在已经被赋值UNSET了,这是和“:-”表达式的区别。
UNSET
[root@kang ~]# result=${test=UNSET} #<==定义时忽略了冒号
[root@kang ~]# echo $result
UNSET  #<==打印结果和带冒号时没有变化
[root@kang ~]# echo $test
UNSET  #<==打印结果和带冒号时没有变化
[root@kang ~]# 





当变量(result)值里的变量(test)值没有定义时,会给变量(result)赋值“:=”后面的内容(UNSET),同时会把“:=”后面

的内容(UNSET)赋值给变量(result)值里没有定义的变量(test)。

这个变量的功能可以解决变量定义的问题,并确保没有定义的变量始终有值。


3.${parameter:?word}功能实践

${parameter:?word}的作用是:如果parameter变量值为空或未赋值,那么Word字符串将被作为标准错误输出,否则输出变量的值。

${parameter:?word}用法功能示例

解释:

[root@kang ~]# echo ${key:?not defined} #<==key变量没有定义,因此把“not defined”作为标准错误输出。
-bash: key: not defined #<==错误提示,只不过是事先定义好的错误输出。
[root@kang ~]# echo ${key?not defined}  #<==去掉冒号定义,并输出,结果一致。
-bash: key: not defined
[root@kang ~]# key=1 #<==给变量赋值1. 
[root@kang ~]# echo ${key:?not defined} #<==因为key有值了,所以,打印key的值1.
1
[root@kang ~]# echo ${key?not defined} #<==去掉冒号定义,并输出,结果一致。
1
[root@kang ~]# unset key #<==取消key的定义。
[root@kang ~]# echo ${key:?not defined}
-bash: key: not defined #<==又打印错误提示了。
[root@kang ~]# 




这个功能可以用于设定由于变量未定义而报错的具体内容,如:“not defined”。

4.${parameter:+word}功能实践

${parameter:+word}的作用是:如果parameter变量值为空或未赋值,则什么都不做,否则Word字符串将替代变量的值。

${parameter:+word}用法功能示例

解释:

[root@kang ~]# blxh=${girl:+word} #<==girl变量未定义
[root@kang ~]# echo $blxh #<==因为girl变量未定义,所以打印blxh变量为空

[root@kang ~]# girl=19 #<==girl变量赋值为19
[root@kang ~]# blxh=${girl:+word} #<==注意,这里一定要重新定义blxh
[root@kang ~]# echo $blxh 
word #<==因为girl变量有值,所以打印blxh变量输出为“:+”后面的内容。
[root@kang ~]# 


这个功能可用于测试变量(girl的位置)是否存在,如果blxh的值为Word,则证明blxh变量有值。


Shell特殊扩展变量的生产场景应用案例


实现Apache服务启动脚本/etc/init.d/httpd(请重点看标记部分)

解释:

#       8 - configuration syntax error
#
# When multiple arguments are given, only the error from the _last_
# one is reported.  Run "apachectl help" for usage info
#
ACMD="$1"
ARGV="$@"
#
# |||||||||||||||||||| START CONFIGURATION SECTION  ||||||||||||||||||||
# --------------------                              --------------------
# 
# the path to your httpd binary, including options if necessary
HTTPD='/usr/sbin/httpd'
HTTPD_LANG=${HTTPD_LANG-"C"} #<==如果HTTPD_LANG变量没有定义或为空,则打印HTTPD_LANG变量返回C值。
#<==这一步定义方法的目的是防止变量值为空或未定义。使用的是(${parameter-word}用法,此处省略了冒号)。
#
#
# a command that outputs a formatted text version of the HTML at the
# url given on the command line.  Designed for lynx, however other
# programs may work.  
if [ -x "/usr/bin/links" ]; then
  LYNX="/usr/bin/links -dump"
else
  LYNX=none
fi
#
httpd=${HTTPD-/usr/sbin/httpd} 
#<==如果HTTPD变量没有定义或为空,则打印HTTPD_LANG变量返回/usr/sbin/httpd的值。
#<==此步定义方法的目的是防止变量值为空或未定义。使用的是(${parameter-Word}用法,冒号省略了)。
# the URL to your server's mod_status status page.  If you do not
# have one, then status and fullstatus will not work.
STATUSURL="http://localhost:80/server-status"

# Source /etc/sysconfig/httpd for $HTTPD setting, etc.
if [ -r /etc/sysconfig/httpd ]; then
   . /etc/sysconfig/httpd
fi

#
                                                                        

提示:通过执行yum -y install httpd后可以查看/etc/init.d/httpd文件。

在企业中,针对目录路径等的处理就可以采用上述变量不存在(即赋值指定)的方式,防止因目录路径不存在而导致的异常。

例如:变量如果为NULL或没有定义,则赋予一个备用的值,特别是针对变量的删除操作,这种方式会很有用,否则删除的变量

如果不存在,则可能导致未知的危险。


删除7天前的过期数据备份

如果忘记了定义path变量,又不希望path值为空值,就可以定义/tmp替代path空值的返回值,如下:

解释:

[root@kang ~]# cat del.sh 
#!/bin/bash
find ${path-/tmp} -name "*.tar.gz" -type f -mtime +7 | xargs rm -rf 
[root@kang ~]# sh -x del.sh 
+ find /tmp -name '*.tar.gz' -type f -mtime +7 #<==执行时,系统会自动删除/tmp下的文件。
+ xargs rm -rf
[root@kang ~]# 

如果忘记定义了path变量,并且还未做特殊变量定义,那么命令就会出现意外,如下:

解释:

[root@kang ~]# cat del.sh 
#!/bin/bash
find ${path} -name "*.tar.gz" -type f -mtime +7 | xargs rm -rf 
[root@kang ~]# sh -x del.sh 
+ find -name '*.tar.gz' -type f -mtime +7 #<==这条命令明显没有指定路径,因此将会导致异常。
+ xargs rm -rf
[root@kang ~]# 

好了,关于shell变量的知识就先演示到这里,这些都是必不可少的基本知识,如果不理解,建议反复去看,去练,最后希望对你

有所帮助!!!!

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值