欢迎您的光临,本博所发布之文章皆为作者亲测通过,如有错误,欢迎通过各种方式指正。由于本站位于香港虚拟主机,故速度比较慢。

教程  Shell&Shell脚本基础教程——变量

Shell 本站 251 0评论

一、Shell组成部分和执行方式


1、shell脚本组成部分

1)注释:#开头

2)命令:shell基本语法

3)变量:shell支持字符串变量和整数变量。

4)结构控制语句:流程控制语句


2、执行方式

两种执行方式:

1)sh shelltest

2)./shelltest

说明:两种执行方式的权限控制不一样。


二、Shell变量


1.注释、指定执行方式、退出


· 注释等说明信息,要以#开头。

· 脚本的第一行调用时表明基于bash的方式执行:#!/bin/bash(说明:如果系统默认是bash执行,不写也行)

· 退出时最好写上:exit 0


示例:

#!/bin/bash
# 我是注释 :-e特殊字符(\a、\n)转义
echo -e "Hello World!\a\n" 
exit 0


2.变量和变量的计算


变量是任何一种编程语言都必不可少的组成部分,变量用来存放各种数据。脚本语言在定义变量时通常不需要指明类型,直接赋值就可以,Shell 变量也遵循这个规则。

在 Bash shell 中,每一个变量的值都是字符串,无论你给变量赋值时有没有使用引号,值都会以字符串的形式存储。

这意味着,Bash shell 在默认情况下不会区分变量类型,即使你将整数和小数赋值给变量,它们也会被视为字符串,这一点和大部分的编程语言不同。

例如在 C/C++ 中,变量分为整数、小数、字符串、布尔等多种类型。

当然,如果有必要,你也可以使用 declare 关键字显式定义变量的类型,但在一般情况下没有这个需求,Shell 开发者在编写代码时自行注意值的类型即可。


1)定义变量

Shell 支持以下三种定义变量的方式:

variable=value
variable='value'
variable="value"

variable 是变量名,value 是赋给变量的值。如果 value 不包含任何空白符(例如空格、Tab 缩进等),那么可以不使用引号;如果 value 包含了空白符,那么就必须使用引号包围起来。使用单引号和使用双引号也是有区别的,稍后我们会详细说明。


注意,赋值号=的周围不能有空格,这可能和你熟悉的大部分编程语言都不一样。


Shell 变量的命名规范:

在定义变量时,有一些规则需要遵守。

1) 变量名可以由字母、数字和下画线组成,但是不能以数字开头。如果变量名是"2name",则是错误的。

2) 在 Bash 中,变量的默认类型都是字符串型,如果要进行数值运算,则必须指定变量类型为数值型。比如:

[root@localhost ~]# aa=1+2
[root@localhost ~]# echo $aa
1+2

看到了吧,变量 aa 的值不是"3",而是"1+2"。在 Bash 中,变量类型是字符串型,所以认为"1+2"只是一个字符串,而不会进数值运算(数值运算方法会在后续章节中介绍)。

3) 变量用等号"="连接值,"="左右两侧不能有空格。这是 Shell 语言特有的格式要求。在绝大多数的其他语言中,"="左右两侧是可以加入空格的。但是在 Shell 中命令的执行格式是"命令 [选项] [参数]",如果在"="左右两侧加入空格,那么 Linux 会误以为这是系统命令,是会报错的。

4) 变量值中如果有空格,则需要使用单引号或双引号包含,如 test="hello world!"。双引号括起来的内容"$"和反引号者都拥有特殊含义,而单引号括起来的内容都是普通字符。

5) 在变量值中,可以使用转义符"\"。

6) 如果需要増加变量值,那么可以进行变量叠加。 例如:

[root@localhost ~]#test=123
[root@localhost ~]#test="$test"
456
[root@localhost ~]# echo $test
123456
#叠加变量test,变量值变成了123456
[root@localhost ~]#test=${test}789
[root@localhost ~]# echo $test
123456789
#再叠加变量test,变量值变成了123456789

变量叠加可以使用两种格式:"$变量名"或 ${变量名}。

7) 如果要把命令的执行结果作为变量值赋予变量,则需要使用反引号或 $() 包含命令。例如:

[root@localhost ~]# test=$(date)
[root@localhost ~]# echo Stest
2013年10月21日 星期一20:27:50 CST

8) 环境变量名建议大写,便于区分。


变量定义举例:

url=http://c.biancheng.net/shell/
echo $url
name='C语言中文网'
echo $name
author="严长生"
echo $author


2)使用变量

使用一个定义过的变量,只要在变量名前面加美元符号 即可,如:

author="严长生"
echo $author
echo ${author}

变量名外面的花括号{ }是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:

skill="Java"
echo "I am good at ${skill}Script"

如果不给 skill 变量加花括号,写成echo "I am good at $skillScript",解释器就会把 $skillScript 当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。


推荐给所有变量加上花括号{ },这是个良好的编程习惯。


3)变量查看

我们可以通过 echo 命令查询已经设定的变量的值,这种查询是已知变量名查询变量值。但是如果我不知道变量名,那么可以查询系统中已经存在的变量吗?


当然可以,只需使用 set 命令即可。set 命令可以用来查看系统中的所有变量(用户自定义变量和环境变量)和设定 Shell 的执行环境。命令格式如下:

[root@localhost ~]# set [选项]

选项:

-u:如果设定此选项,则在调用未声明的变量时会报错(默认无任何提示);

-x:如果设定此选项,则在命令执行之前会先把命令输出一次;

举几个例子:

[root@localhost ~]# set
BASH=/bin/bash
...省略部分输出...
name='C biancheng'
#直接使用set命令,会查询系统中所有的变量,包含用户自定义变量和环境变量
[root@localhost ~]# set -u
[root@localhost ~]# echo $file
-bash: file: unbound variable
#当设置了-u选项后,如果调用没有设定的变量则会报错。默认是没有任何输出的
[root@localhost ~]# set -x
[root@localhost ~]# ls
+ ls --color=auto
anaconda-ks.cfg install.log install.log.syslog sh tdir test testfile
#如果设定了-x选项,则会在每条命令执行之前先把命令输出一次


set 命令的选项和功能众多,不过我们更常用的还是使用 set 命令查看变量。


4)修改变量的值

已定义的变量,可以被重新赋值,如:

url="http://c.biancheng.net"
echo ${url}
url="http://c.biancheng.net/shell/"
echo ${url}

第二次对变量赋值时不能在变量名前加$,只有在使用变量时才能加$。


5)单引号和双引号的区别

前面我们还留下一个疑问,定义变量时,变量的值可以由单引号' '包围,也可以由双引号" "包围,它们到底有什么区别呢?不妨以下面的代码为例来说明:

#!/bin/bash
url="http://c.biancheng.net"
website1='C语言中文网:${url}'
website2="C语言中文网:${url}"
echo $website1
echo $website2

运行结果:

C语言中文网:${url}

C语言中文网:http://c.biancheng.net


· 以单引号' '包围变量的值时,单引号里面是什么就输出什么,即使内容中有变量和命令(命令需要反引起来)也会把它们原样输出。这种方式比较适合定义显示纯字符串的情况,即不希望解析变量、命令等的场景。

· 以双引号" "包围变量的值时,输出时会先解析里面的变量和命令,而不是把双引号中的变量名和命令原样输出。这种方式比较适合字符串中附带有变量和命令并且想将其解析后再输出的变量定义。


我的建议:如果变量的内容是数字,那么可以不加引号;如果真的需要原样输出就加单引号;其他没有特别要求的字符串等最好都加上双引号,定义变量时加双引号是最常见的使用场景。


6)将命令的结果赋值给变量

Shell 也支持将命令的执行结果赋值给变量,常见的有以下两种方式:

variable=`command`
variable=$(command)

第一种方式把命令用反引号` `(位于 Esc 键的下方)包围起来,反引号和单引号非常相似,容易产生混淆,所以不推荐使用这种方式;第二种方式把命令用$()包围起来,区分更加明显,所以推荐使用这种方式。

例如,我在 code 目录中创建了一个名为 log.txt 的文本文件,用来记录我的日常工作。下面的代码中,使用 cat 命令将 log.txt 的内容读取出来,并赋值给一个变量,然后使用 echo 命令输出。

$ cd code
$ log=$(cat log.txt)
$ echo $log
[2018-09-10 06:53:22] 严长生正在编写Shell教程
$ log=`cat log.txt`
$ echo $log
[2018-09-10 06:53:22] 严长生正在编写Shell教程
$

 

7)只读变量

使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。


下面的例子尝试更改只读变量,结果报错:

#!/bin/bash
myUrl="http://see.xidian.edu.cn/cpp/shell/"
readonly myUrl
myUrl="http://see.xidian.edu.cn/cpp/danpianji/"
运行脚本,结果如下:
/bin/sh: NAME: This variable is read only.


8)删除变量

使用 unset 命令可以删除变量。语法:

unset variable_name

变量被删除后不能再次使用;unset 命令不能删除只读变量。


举个例子:

#!/bin/sh
myUrl="http://c.biancheng.net/shell/"
unset myUrl
echo $myUrl

上面的脚本没有任何输出。


3.变量的作用域


Shell 变量的作用域可以分为三种:

· 有的变量可以在当前 Shell 会话中使用,这叫做全局变量(global variable);

· 有的变量只能在函数内部使用,这叫做局部变量(local variable);

· 而有的变量还可以在其它 Shell 中使用,这叫做环境变量(environment variable)。


1)全局变量

所谓全局变量,就是指变量在当前的整个 Shell 会话中都有效。每个 Shell 会话都有自己的作用域,彼此之间互不影响。在 Shell 中定义的变量,默认就是全局变量。

想要实际演示全局变量在不同 Shell 会话中的互不相关性,可在图形界面下同时打开两个 Shell,或使用两个终端远程连接到服务器(SSH)。


首先打开一个 Shell 窗口,定义一个变量 a 并赋值为 1,然后打印,这时在同一个 Shell 窗口中是可正确打印变量 a 的值的。然后再打开一个新的 Shell 窗口,同样打印变量 a 的值,但结果却为空,如图 1 所示。

111.png

图1:打开两个 Shell 会话


这说明全局变量 a 仅仅在定义它的第一个 Shell 中有效,对其它 Shell 没有影响。这很好理解,就像小王家和小徐家都有一部电视机(变量名相同),但是同一时刻小王家和小徐家的电视中播放的节目可以是不同的(变量值不同)。


需要强调的是,全局变量的作用范围是当前的 Shell 会话,而不是当前的 Shell 脚本文件,它们是不同的概念。打开一个 Shell 窗口就创建了一个 Shell 会话,打开多个 Shell 窗口就创建了多个 Shell 会话,每个 Shell 会话都是独立的进程,拥有不同的进程 ID。在一个 Shell 会话中,可以执行多个 Shell 脚本文件,此时全局变量在这些脚本文件中都有效。


例如,现在有两个 Shell 脚本文件,分别是 a.sh 和 b.sh。a.sh 的代码如下:

#!/bin/bash
echo $a
b=200

b.sh 的代码如下:

#!/bin/bash
echo $b


打开一个 Shell 窗口,输入以下命令:

$ a=99
$ . ./a.sh
99
$ . b.sh
200
$

从输出结果可以发现,在 Shell 会话中以命令行的形式定义的变量 a,在 a.sh 中有效;在 a.sh 中定义的变量 b,在 b.sh 中也有效。


2)局部变量

Shell 也支持自定义函数,但是 Shell 函数和 C/C++、Java 等其他编程语言函数的一个不同点就是:在 Shell 函数中定义的变量默认也是全局变量,它和在函数外部定义变量拥有一样的效果。请看下面的代码:

#!/bin/bash
#定义函数
function func(){
    a=99
}
#调用函数
func
#输出函数内部的变量
echo $a

输出结果:

99

a 是在函数内部定义的,但是在函数外部也可以得到它的值,证明它的作用域是全局的,而不是仅限于函数内部。


要想变量的作用域仅限于函数内部,那么可以在定义时加上local命令,此时该变量就成了局部变量。请看下面的代码:

#!/bin/bash
#定义函数
function func(){
    local a=99
}
#调用函数
func
#输出函数内部的变量
echo $a

输出结果为空,表明变量 a 在函数外部无效,是一个局部变量。


Shell 变量的这个特性和 JavaScript 中的变量是类似的。在 JavaScript 函数内部定义的变量,默认也是全局变量,只有加上var关键字,它才会变成局部变量。


3)环境变量

全局变量只在当前 Shell 会话中有效,如果使用export命令将它导出,那么它就在所有的子 Shell 中也有效了,这称为“环境变量”。


环境变量被创建时所处的 Shell 被称为父 Shell,如果在父 Shell 中再创建一个 Shell,则该 Shell 被称作子 Shell。当子 Shell 产生时,它会继承父 Shell 的环境变量为自己所用,所以说环境变量可从父 Shell 传给子 Shell。不难理解,环境变量还可以传递给孙 Shell。

注意,环境变量只能向下传递而不能向上传递,即“传子不传父”。


在一个 Shell 中创建子 Shell 最简单的方式是运行 bash 命令,如图 2 所示。

222.png

图2:进入子 Shell


通过exit命令可以一层一层地退出 Shell。


下面演示一下环境变量的使用:

$ a=22      #定义一个全局变量
$ echo $a    #在当前Shell中输出a,成功
22
$ bash    #进入子Shell
$ echo $a    #在子Shell中输出a,失败
$ exit    #退出子Shell
exit
$ export a    #将a导出为环境变量
$ bash    #重新进入子Shell
$ echo $a    #在子Shell中再次输出a,成功
22
$ exit    #退出子Shell
exit
$ exit    #退出父Shell,结束整个Shell会话

可以发现,默认情况下,a 在子 Shell 中是无效的;使用 export 将 a 导出为环境变量后,在子 Shell 中就可以使用了。

export a这种形式是在定义变量 a 以后再将它导出为环境变量,如果想在定义的同时导出为环境变量,可以写作export a=22。


注意,本节我们一直强调的是环境变量在子 Shell 中有效,并没有说它在所有的 Shell 种有效;如果你通过终端创建一个新的 Shell,那么它就不是当前 Shell 的子 Shell,环境变量对这个 Shell 就是无效的。


此外,通过export命令导出的环境变量是临时的,关闭 Shell 会话后它就销毁了。所以,这种环境变量也只是在局部范围内起作用,并不影响所有 Shell。


如果想让环境变量在所有 Shell 中都有效,并且能够永久保存,在关闭 Shell 后也不丢失,那么就需要把环境变量写入启动文件。至于如何写入文件,请大家自行百度,这里就不再赘述了。


4.Shell位置参数(参数的传递)


运行 Shell 脚本文件时我们可以给它传递一些参数,这些参数在脚本文件内部可以使用$n的形式来接收,例如,$1 表示第一个参数,$2 表示第二个参数,依次类推。

同样,在调用函数时也可以传递参数。Shell 函数参数的传递和其它编程语言不同,没有所谓的形参和实参,在定义函数时也不用指明参数的名字和数目。换句话说,定义 Shell 函数时不能带参数,但是在调用函数时却可以传递参数,这些传递进来的参数,在函数内部就也使用$n的形式接收,例如,$1 表示第一个参数,$2 表示第二个参数,依次类推。


以下实例我们向脚本传递三个参数,并分别输出,其中 $0 为执行的文件名:

#!/bin/bash
echo "Shell 传递参数实例!";
echo "执行的文件名:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";

为脚本设置可执行权限,并执行脚本,输出结果如下所示:

$ chmod +x test.sh 
$ ./test.sh 1 2 3

输出如下: 

Shell 传递参数实例!

执行的文件名:./test.sh

第一个参数为:1

第二个参数为:2

第三个参数为:3


这种通过$n的形式来接收的参数,在 Shell 中称为位置参数


在讲解变量的命名时,我们提到:变量的名字必须以字母或者下划线开头,不能以数字开头;但是位置参数却偏偏是数字,这和变量的命名规则是相悖的,所以我们将它们视为“特殊变量”。


除了 $n,Shell 中还有 $#、$*、$@、$?、$$ 几个特殊参数,我们将在后面讲解。


1) 给脚本文件传递位置参数

请编写下面的代码,并命名为 test.sh:

#!/bin/bash
echo "Language: $1"
echo "URL: $2"

运行 test.sh,并附带参数:

$ . ./a.sh Shell http://c.biancheng.net/shell/

运行结果:

Language: Shell

URL: http://c.biancheng.net/shell/

其中Shell是第一个位置参数,http://c.biancheng.net/shell/是第二个位置参数。


2) 给函数传递位置参数

请编写下面的代码,并命名为 test.sh:

#!/bin/bash
#定义函数
function func(){
    echo "Language: $1"
    echo "URL: $2"
}
#调用函数
func C++ http://c.biancheng.net/cplus/

运行 test.sh:

$ . ./a.sh

运行结果:

Language: C++

URL: http://c.biancheng.net/cplus/


关于函数定义和调用的具体语法,后续我们还会详细讲解,这里大家了解一下即可。

注意事项

如果参数个数太多,达到或者超过了 10 个,那么就得用${n}的形式来接收了,例如 ${10}、${23}。{ }的作用是为了帮助解释器识别参数的边界,这跟使用变量时加{ }是一样的效果。


5.Shell特殊变量:Shell $#、$*、$@、$?、$$


上面我们讲到了 $n,它是特殊变量的一种,用来接收位置参数。下面我们继续讲解剩下的几个特殊变量,它们分别是:$#、$*、$@、$?、$$。


Shell 特殊变量及其含义
变量含义
$0当前脚本的文件名。
$n(n≥1)传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是 $1,第二个参数是 $2。
$#传递给脚本或函数的参数个数。
$*传递给脚本或函数的所有参数。
$@传递给脚本或函数的所有参数。当被双引号" "包含时,$@ 与 $* 稍有不同,我们将在下文讲解。
$?上个命令的退出状态,或函数的返回值,我们将在下文讲解。
$$当前 Shell 进程 ID。对于 Shell 脚本,就是这些脚本所在的进程 ID。


下面我们通过两个例子来演示。


1) 给脚本文件传递参数

编写下面的代码,并保存为 test.sh:

#!/bin/bash
echo "Process ID: $$"
echo "File Name: $0"
echo "First Parameter : $1"
echo "Second Parameter : $2"
echo "All parameters 1: $@"
echo "All parameters 2: $*"
echo "Total: $#"

运行 test.sh,并附带参数:

$ chmod +x ./a.sh
$ ./test.sh Shell Linux

运行结果为:

Process ID: 2788

File Name: ./test.sh

First Parameter : Shell

Second Parameter : Linux

All parameters 1: Shell Linux

All parameters 2: Shell Linux

Total: 2


2) 给函数传递参数

编写下面的代码,并保存为 test.sh:

#!/bin/bash
#定义函数
function func(){
    echo "Language: $1"
    echo "URL: $2"
    echo "First Parameter : $1"
    echo "Second Parameter : $2"
    echo "All parameters 1: $@"
    echo "All parameters 2: $*"
    echo "Total: $#"
}
#调用函数
func Java http://c.biancheng.net/java/

运行结果为:

Language: Java

URL: http://c.biancheng.net/java/

First Parameter : Java

Second Parameter : http://c.biancheng.net/java/

All parameters 1: Java http://c.biancheng.net/java/

All parameters 2: Java http://c.biancheng.net/java/

Total: 2


6.Shell $*和$@的区别


$* 和 $@ 都表示传递给函数或脚本的所有参数,在上面Shell特殊变量中进行了演示,下面重点说一下它们之间的区别。


当 $* 和 $@ 不被双引号" "包围时,它们之间没有任何区别,都是将接收到的每个参数看做一份数据,彼此之间以空格来分隔。


但是当它们被双引号" "包含时,就会有区别了:

· "$*"会将所有的参数从整体上看做一份数据,而不是把每个参数都看做一份数据。

· "$@"仍然将每个参数都看作一份数据,彼此之间是独立的。

比如传递了 5 个参数,那么对于"$*"来说,这 5 个参数会合并到一起形成一份数据,它们之间是无法分割的;而对于"$@"来说,这 5 个参数是相互独立的,它们是 5 份数据。


如果使用 echo 直接输出"$*"和"$@"做对比,是看不出区别的;但如果使用 for 循环来逐个输出数据,立即就能看出区别来。


编写下面的代码,并保存为 test.sh:

#!/bin/bash
echo "print each param from \"\$*\""
for var in "$*"
do
    echo "$var"
done
echo "print each param from \"\$@\""
for var in "$@"
do
    echo "$var"
done

运行 test.sh,并附带参数:

$ . ./test.sh a b c d

运行结果:

print each param from "$*"

a b c d

print each param from "$@"

a

b

c

d

从运行结果可以发现,对于"$*",只循环了 1 次,因为它只有 1 分数据;对于"$@",循环了 5 次,因为它有 5 份数据。


7.Shell $?:获取函数返回值或者上一个命令的退出状态


$? 是一个特殊变量,用来获取上一个命令的退出状态,或者上一个函数的返回值。

所谓退出状态,就是上一个命令执行后的返回结果。退出状态是一个数字,一般情况下,大部分命令执行成功会返回 0,失败返回 1,这和C语言的 main() 函数是类似的。


不过,也有一些命令返回其他值,表示不同类型的错误。


1) $? 获取上一个命令的退出状态

我们使用两个脚本文件来演示。


先编写下面的代码,并保存为 a.sh:

#!/bin/bash
if [ $1 == 100 ]
then
   return 0  #参数正确,返回0
else
   return 1  #参数错误,返回1
fi

再编写下面的代码,并保存为 b.sh:

#!/bin/bash
echo $?

先运行 a.sh,传递参数 100,然后再运行 b.sh,结果如下:

$ . ./a.sh 100
$ . ./b.sh
0

如果将传递给 a.sh 的参数改为 89,b.sh 的运行结果就不同了:

$ . ./a.sh 89
$ . ./b.sh
1


2) $? 获取函数的返回值

编写下面的代码,并保存为 test.sh:

#!/bin/bash
#得到两个数相加的和
function add(){
    return `expr $1 + $2`
}
add 23 50  #调用函数
echo $?  #获取函数返回值

运行 test.sh:

$ . ./test.sh
73


原文地址:http://c.biancheng.net/view/955.html

转载请注明: ITTXX.CN--分享互联网 » Shell&Shell脚本基础教程——变量

最后更新:2018-12-25 16:36:11

赞 (2) or 分享 ()
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽