Shell 变量(一)
bash shell 编程和其他编程语言差不多,同样包含变量(存放字符串和数值的容器,可以进行修改、比较、传递)。在引用 bash 变量时,可以使用一些非常特殊的运算符。bash 还拥有内建变量,这些变量可以提供有关脚本中其他变量的重要信息。下面介绍 bash 变量和一些特殊的变量引用机制,展示如何将其运用于你自己的脚本。
1、shell 变量基础知识
bash 脚本中的变量名称通常采用全大写,但这并非强制性的,只是一种常见做法而已。变量不用事先声明,直接使用就行了。变量基本上都是字符串类型,不过有些运算符能够将变量内容视为数字。变量的实际用法如下所示。
# 使用shell变量的普通脚本
MYVAR="something"
echo $MYVAR
# 写法类似,但没有引号
MY_2ND=anotherone
echo $MY_2ND
# 这里因为包含空客,需要使用引号:
MYOTHER="more stuff to echo"
echo $MYOTHER
bash 变量的语法有两处要点,但可能不那么一目了然。
- 首先,赋值语法 name=value 看起来相当直观,但 = 两侧不能有任何空白字符。如果允许 = 两侧出现空白字符,那么变量赋值就会变成下面这样:
MYVAR = something
此时 shell 很难区分出到底是要调用命令还是要给变量赋值。对于能够以 = 为参数的命令(如 test)更是如此。所以,还是让事情简单点吧:变量赋值时,shell 不允许在 = 两侧出现空白字符。该规定的另一方面也值得注意,不要在文件名中使用 =。
-
其次需要注意的是,引用变量时要使用 $ 符号。给变量赋值时不需要在变量名前加 $,但获取变量值时需要。出现在表达式 $(( ))中的变量是个例外。原因很简单,就是为了消除歧义。如下:
MYVAR=something echo MYVAR is now MYVAR
你能分辨出哪个是字符串 MYVAR,哪个是变量 MYVAR 吗?bash 中的一切都是字符串,所以需要用 $ 来表明变量引
用。
2、记录脚本
详细讨论 shell 脚本或变量前,我们还得说说如何记录脚本。毕竟,你得能看明白自己的脚本,即便是在编写完的几个月后。
用注释记录脚本。# 代表注释的开始。该行上随后的所有字符都会被shell 忽略。
#
# 这是一行注释
#
# 多用注释
# 注释是你的好朋友
如果您是java开发工作者,你会发现,这就是我们平时常说的代码注释。
3、将变量名与周围的文本分开
如果你需要输出变量以及其他文本。引用变量要用到 $,但是该怎么区分变量名与紧随其后的其他文本呢?例如,你想要用 shell 变量作为文件名的一部分,如下所示:
for FN in 1 2 3 4 5
do
somescript /tmp/rep$FNport.txt #执行某个脚本,把文件当作执行参数,如cat
done
shell 会怎么理解这段代码?它会认为变量名从 $ 开始,到点号结束。换句话说,它将 $FNport 视为变量名,而非我们想要的 $FN。
那么,我们如何让shell知道我们的变量是FN呢?
使用完整的变量引用语法,不仅要包括 $,还要在变量名周围加上花括号,如下:
somescript /tmp/rep${FN}port.txt
因为 shell 变量名中只能包含字母、数字以及下划线,所以很多时候并不需要使用花括号。任何空白字符或标点符号(下划线除外)都足以提示变量名的结束位置。但只要有疑问,就应该用花括号。
4、导出变量
你在某个脚本中定义了一个变量,但在调用其他脚本时,该脚本并不知道这个变量的存在。为了解决这个问题,我们需要将传给其他脚本的变量导出。如下所示:
export MYVAR
export NAME=value
要想查看所有已导出的变量,敲入命令 env(或者内建命令 export-p)就能列出各个变量及其值。当脚本运行时,这些变量都可供使用,其中很多是 bash 启动脚本已经设置好的,如$PATH。
可以在 export 后面跟上变量赋值,不过这种写法不适用于比较老的 shell 版本。然后导出之后,就可以随意给变量赋值,不用重复导出。因此,有时你会看到下列语句:
# 导出变量
export FNAME
export SIZE
export MAX
# 为变量赋值
MAX=2048
SIZE=64
FNAME=/tmp/scratch
注意,导出的变量实际上是按值调用的。在被调用脚本中修改导出变量的值并不会改变调用脚本中该变量的值。
对于导出的变量,我们该如何删除呢?
# 删除变量
unset myvar
Shell 变量(二)
你希望用户能在调用脚本时指定参数。可以要求用户设置一个 shell变量,但这种做法似乎不够灵活。另外还需要向其他脚本传递数据。这可以通过环境变量实现,但会使两个脚本之间的联系过于紧密。因此,此处我们可以用到脚本参数。
1、在shell脚本中使用参数
使用命令行参数。在命令行上,出现在脚本名之后的任意单词都可以在脚本中作为编号变量(numbered variable)被访问。假设有下列脚本 simplest.sh。
# 一个简单的shell脚本
echo $1
该脚本会显示在命令行上被调用时所指定的第一个参数。我们来看一种实际用法。
$ cat simplest.sh
# 一个简单的shell脚本
echo ${1}
$ ./simplest.sh you see what I mean
you
$ ./simplest.sh one more time
one
$
其他参数的可用形式分别为 ${2}、${3}、${4}、${5} 等。单个数位的数字用不着花括号,除非要区分变量名与其后出现的文本。典型的脚本只用到少部分参数,但如果涉及 ${10},那就得使用花括号了,否则 shell 会将 $10 理解为 ${1} 后面紧跟着字符串 0,如下所示。
$ cat tricky.sh
echo $1 $10 ${10}
$ ./tricky.sh I II III IV V VI VII VIII IX X XI
I I0 X #注意观察第二个输出
$
第 10 个参数的值是 X,但如果在脚本中写成 $10,那么你在 echo语句中得到的会是第一个参数 $1,后面紧跟着一个字符串 0。
因为第三个使用了${},所以三个${10}可以正常输出X。
2、遍历传入脚本的参数: $*
如果你想对指定的一系列参数执行某些操作。在编写 shell 脚本时,对单个参数进行处理不是什么问题,只需要用 $1 引用这个参数即可。但如果面对的是一大批文件呢?你可能想这样调用脚本。
./actall *.txt
shell 会进行模式匹配,生成匹配 *.txt 模式(以 .txt 结尾的文件名)的文件名列表。对于脚本而言,我们永远无法预估传入的参数的个数,那么我们就无法通过${数字}获取所有参数,那么${数字}方式将不再适用。
特殊的 shell 变量 $* 能够引用所有的参数,可以将其用于 for 循环,如下所示:
#!/usr/bin/env bash
# 实例文件:actall.sh
# 批量修改文件权限
#
for FN in $*
do
echo changing $FN
chmod 0750 $FN
done
变量 $FN 是我们自己挑选的;使用别的变量名也没有任何问题。$*引用的是命令行上出现的所有参数。假如用