高级Bash脚本编程指南(3):变量和参数的介绍
成于坚持,败于止步
变量替换
变量的名字就是变量保存值的地方. 引用变量的值就叫做变量替换.“$“这个符号就好像是一种标志
让我们仔细的区别变量的名字和变量的值. 如果variable是一个变量的名字, 那么$variable就是引用这变量的值, 即这边变量所包含的数据.
root@ubuntu:~# variable=12 root@ubuntu:~# echo variable variable root@ubuntu:~# echo $variable 12当变量没有$前缀的时候,那么变量可能存在如下几种情况.
1.变量被声明或被赋值,
2.变量被unset,
3.变量被exporte,
4.变量处在一种特殊的情况,变量代表一种信号
变量赋值可以使用=(比如 var1=27), 也可以在read命令中或者循环头进行赋值 (for var2 in 1 2 3).
被一对双引号(" ")括起来的变量替换是不会被阻止的. 所以双引号被称为部分引用, 有时候又被称为"弱引用". 但是如果使用单引号的话(' '), 那么变量替换就会被禁止了, 变量名只会被解释成字面的意思, 不会发生变量替换. 所以单引号被称为全引用, 有时候也被称为"强引用".
注意$variable事实上只是${variable}的简写形式. 在某些上下文中$variable可能会引起错误, 这时候你就需要用${variable}了
下面以一个例子做说明
#!/bin/bash # 变量赋值和替换 a=375 #这里没有$说明是声明并且赋值了 hello=$a #$a表示使用变量a的值,把这个值赋值给hello这个变量 #------------------------------------------------------------------------- # 强烈注意, 在赋值的的时候, 等号前后一定不要有空格. # 如果出现空格会怎么样? # "VARIABLE =value" # 脚本将尝试运行一个"VARIABLE"的命令, 带着一个"=value"参数. # "VARIABLE= value" # 脚本将尝试运行一个"value"的命令, 并且带着一个被赋值成""的环境变量"VARIABLE". #------------------------------------------------------------------------- echo hello #没有$这里打印hello变量的名字 echo $hello #打印hello变量的值 echo ${hello} #同样是打印hello变量的值 echo "$hello" echo "${hello}" hello="A B C D" #重新赋值 echo $hello # A B C D 引用一个变量将保留其中的空白, 当然, 如果是变量替换就不会保留了. echo "$hello" # A B C D echo '$hello' hello= echo "\$hello (null value) = $hello" V3=23 var1=21 var2=22 var3=$V3 echo "var1=$var1 var2=$var2 var3=$var3" numbers="one two three" other_numbers="1 2 3" echo "numbers = $numbers" echo "other_numbers = $other_numbers" mixed_bag=2\ ---\ Whatever echo "$mixed_bag" echo "uninitialized_variable = $uninitialized_variable" uninitialized_variable= echo "uninitialized_variable = $uninitialized_variable" uninitialized_variable=23 unset uninitialized_variable echo "uninitialized_variable = $uninitialized_variable" exit 0看看这个脚本的执行结果:
root@ubuntu:~/resource/study/shell_study# ./value-test hello 375 375 375 375 A B C D A B C D $hello $hello (null value) = var1=21 var2=22 var3=23 numbers = one two three other_numbers = 1 2 3 2 --- Whatever uninitialized_variable = uninitialized_variable = uninitialized_variable =
一个未初始化的变量将会是"null"值 - 就是未赋值(但并不是代表值是0!). 在给变量赋值之前就使用这个变量通常都会引起问题.
但是在执行算术操作的时候, 仍然有可能使用未初始化过的变量
root@ubuntu:~/resource/study/shell_study# echo "$data" root@ubuntu:~/resource/study/shell_study# let "data +=5" root@ubuntu:~/resource/study/shell_study# echo "$data" 5
结论:一个未初始化的变量是没有值的, 但是在做算术操作的时候, 这个未初始化的变量看起来值为0. 这是一个未文档化(并且可能不具可移植性)的行为.
赋值操作(前后都不能有空白)
因为=和-eq都可以用做条件测试操作, 所以不要与这里的赋值操作相混淆.
注意: =既可以用做条件测试操作, 也可以用于赋值操作, 这需要视具体的上下文而定.
简单的赋值操作举例:
#!/bin/bash # 赋值 a=879 echo "The value of \"a\" is $a." # 使用'let'赋值 let a=16+5 echo "The value of \"a\" is now $a." # 在'for'循环中(事实上, 这是一种伪赋值): echo -n "Values of \"a\" in the loop are: " #这里加上-n表示忽略string最后的换行操作,否则会换行 for a in 7 8 9 11 do echo -n "$a " done echo # 使用'read'命令进行赋值(这也是一种赋值的类型): echo -n "Enter \"a\": " read a #读取输入的值 echo "The value of \"a\" is now $a." exit 0实验结果:
root@ubuntu:~/resource/study/shell_study# ./value-test1 The value of "a" is 879. The value of "a" is now 21. Values of "a" in the loop are: 7 8 9 11 Enter "a": 121212 The value of "a" is now 121212.再看一个稍微复杂一点的例子:
#!/bin/bash a=23 # 简单的赋值 echo $a b=$a echo $b # 现在让我们来点小变化(命令替换). a=`echo Hello!` # 把'echo'命令的结果传给变量'a' echo $a a=`ls -l` # 把'ls -l'的结果赋值给'a' echo $a # 然而, 如果没有引号的话将会删除ls结果中多余的tab和换行符. echo echo "$a" # 如果加上引号的话, 那么就会保留ls结果中的空白符. exit 0看一看实验结果:
root@ubuntu:~/resource/study/shell_study# chmod 777 value-test2 root@ubuntu:~/resource/study/shell_study# ./value-test2 23 23 Hello! total 48 -rwxrwxrwx 1 root root 663 2013-04-22 03:34 clear_log -rw-r--r-- 1 root root 354 2013-04-22 03:15 data-file -rwxrwxrwx 1 root root 404 2013-04-22 05:05 for_test -rwxrwxrwx 1 root root 345 2013-04-22 03:16 include_file -rwxrwxrwx 1 root root 831 2013-04-22 04:08 para_sub -rwxrwxrwx 1 root root 253 2013-04-22 00:35 show_self -rwxrwxrwx 1 root root 256 2013-04-22 04:41 test1 -rwxrwxrwx 1 root root 204 2013-04-22 04:50 test2 -rw-r--r-- 1 root root 59 2013-04-22 04:50 test-context -rwxrwxrwx 1 root root 1221 2013-04-23 22:33 value-test -rwxrwxrwx 1 root root 415 2013-04-23 22:51 value-test1 -rwxrwxrwx 1 root root 439 2013-04-23 22:57 value-test2 total 48 -rwxrwxrwx 1 root root 663 2013-04-22 03:34 clear_log -rw-r--r-- 1 root root 354 2013-04-22 03:15 data-file -rwxrwxrwx 1 root root 404 2013-04-22 05:05 for_test -rwxrwxrwx 1 root root 345 2013-04-22 03:16 include_file -rwxrwxrwx 1 root root 831 2013-04-22 04:08 para_sub -rwxrwxrwx 1 root root 253 2013-04-22 00:35 show_self -rwxrwxrwx 1 root root 256 2013-04-22 04:41 test1 -rwxrwxrwx 1 root root 204 2013-04-22 04:50 test2 -rw-r--r-- 1 root root 59 2013-04-22 04:50 test-context -rwxrwxrwx 1 root root 1221 2013-04-23 22:33 value-test -rwxrwxrwx 1 root root 415 2013-04-23 22:51 value-test1 -rwxrwxrwx 1 root root 439 2013-04-23 22:57 value-test2
Bash变量是不区分类型的
不像其他程序语言一样, Bash并不对变量区分"类型". 本质上, Bash变量都是字符串. 但是依赖于具体的上下文, Bash也允许比较操作和整数操作. 其中的关键因素就是, 变量中的值是否只有数字.
下面看一下一个实例;
#!/bin/bash a=2334 # 整型. let "a += 1" echo "a = $a " # a = 2335 b=${a/23/BB} # 将"23"替换成"BB". echo "b = $b" # b = BB35 declare -i b # 即使使用declare命令也不会对此有任何帮助. echo "b = $b" # b = BB35 let "b += 1" # BB35 + 1 = echo "b = $b" # b = 1 c=BB34 echo "c = $c" # c = BB34 d=${c/BB/23} # 将"BB"替换成"23". # 这使得变量$d变为一个整形. echo "d = $d" # d = 2334 let "d += 1" # 2334 + 1 = echo "d = $d" # d = 2335 # null变量会如何呢? e="" echo "e = $e" # e = let "e += 1" # 算术操作允许一个null变量? echo "e = $e" # e = 1 # 如果没有声明变量会怎样? echo "f = $f" # f = let "f += 1" # 算术操作能通过么? echo "f = $f" # f = 1 # 所以说Bash中的变量都是不区分类型的. exit 0看一看他是执行结果:
root@ubuntu:~/resource/study/shell_study# ./int-char a = 2335 b = BB35 b = BB35 b = 1 c = BB34 d = 2334 d = 2335 e = e = 1 f = f = 1
不区分变量的类型既是幸运的事情也是悲惨的事情. 它允许你在编写脚本的时候更加的灵活(但是也足够把你搞晕!), 并且可以让你能够更容易的编写代码. 然而, 这也很容易产生错误, 并且让你养成糟糕的编程习惯.
这样的话, 程序员就承担了区分脚本中变量类型的责任. Bash是不会为你区分变量类型的.
特殊的变量类型
局部变量
这种变量只有在代码块或者函数中(参见函数中的局部变量)才可见.
环境变量
这种变量将影响用户接口和shell的行为
在通常情况下, 每个进程都有自己的"环境", 这个环境是由一组变量组成的, 这些变量中存有进程可能需要引用的信息. 在这种情况下, shell与一个一般的进程没什么区别.
每次当一个shell启动时, 它都将创建适合于自己环境变量的shell变量. 更新或者添加一个新的环境变量的话, 这个shell都会立刻更新它自己的环境(译者注: 换句话说, 更改或增加的变量会立即生效), 并且所有的shell子进程(即这个shell所执行的命令)都会继承这个环境. (译者注: 准确地说, 应该是后继生成的子进程才会继承Shell的新环境变量, 已经运行的子进程并不会得到它的新环境变量).
分配给环境变量的空间是有限的. 创建太多环境变量, 或者给一个环境变量分配太多的空间都会引起错误.
如果一个脚本要设置一个环境变量, 那么需要将这些变量"export"出来, 也就是需要通知到脚本本地的环境. 这是export命令的功能.
一个脚本只能够export变量到这个脚本所产生的子进程, 也就是说只能够对这个脚本所产生的命令和进程起作用. 如果脚本是从命令行中调用的, 那么这个脚本所export的变量是不能影响命令行环境的. 也就是说, 子进程是不能够export变量来影响产生自己的父进程的环境的.
位置参数
从命令行传递到脚本的参数: $0, $1, $2, $3 . . .
$0就是脚本文件自身的名字, $1 是第一个参数, $2是第二个参数, $3是第三个参数, 然后是第四个. $9之后的位置参数就必须用大括号括起来了, 比如, ${10}, ${11}, ${12}.
两个比较特殊的变量$*和$@ 表示所有的位置参数.
还是看一个实例吧
#!/bin/bash # 作为用例, 调用这个脚本至少需要10个参数, 比如: # ./scriptname 1 2 3 4 5 6 7 8 9 10 MINPARAMS=10 echo "The name of this script is \"$0\"." echo "The name of this script is \"`basename $0`\"." if [ -n "$1" ] # 测试变量被引用. then echo "Parameter #1 is $1" # 需要引用才能够转义"#" fi if [ -n "$2" ] then echo "Parameter #2 is $2" fi if [ -n "$3" ] then echo "Parameter #3 is $3" fi if [ -n "${10}" ] # 大于$9的参数必须用{}括起来. then echo "Parameter #10 is ${10}" fi echo "-----------------------------------" echo "All the command-line parameters are: "$*"" if [ $# -lt "$MINPARAMS" ] then echo "This script needs at least $MINPARAMS command-line arguments!" fi exit 0看看实验结果:
root@ubuntu:~/resource/study/shell_study# ./args 1 2 3 The name of this script is "./args". The name of this script is "args". Parameter #1 is 1 Parameter #2 is 2 Parameter #3 is 3 ----------------------------------- All the command-line parameters are: 1 2 3 This script needs at least 10 command-line arguments! root@ubuntu:~/resource/study/shell_study# ./args 1 2 3 4 5 6 7 8 9 10 11 The name of this script is "./args". The name of this script is "args". Parameter #1 is 1 Parameter #2 is 2 Parameter #3 is 3 Parameter #10 is 10 ----------------------------------- All the command-line parameters are: 1 2 3 4 5 6 7 8 9 10 11{}标记法提供了一种提取从命令行传递到脚本的最后一个位置参数的简单办法
一些脚本可能会依赖于使用不同的调用名字, 来表现出不同的行为. 如果想要达到这种目的, 一般都需要在脚本中检查$0. 因为脚本只能够有一个真正的文件名, 如果要产生多个名字, 必须使用符号链接.
看一下shift命令的使用实例:
#!/bin/bash # 使用'shift'来逐步存取所有的位置参数. 给脚本命个名, 比如shft,然后给脚本传递一些位置参数, 比如: ./shft a b c def 23 skidoo until [ -z "$1" ] # 直到所有的位置参数都被存取完... do echo -n "$1 " shift done echo exit 0查看实验结果:
root@ubuntu:~/resource/study/shell_study# ./shift d df lsjf sldjf d df lsjf sldjf
先到这里了,O(∩_∩)O~
我的专栏地址:http://blog.csdn.net/column/details/shell-daily-study.html
待续。。。。