高级Bash脚本编程指南(3):变量和参数的介绍

高级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

待续。。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值