zoukankan      html  css  js  c++  java
  • Shell变量详解

      参考:http://www.zsythink.net/archives/279

      这篇文章总结了怎样在bash shell中创建变量、使用变量、删除变量,并且演示了本地变量(全局变量和局部变量)、环境变量、只读变量和特殊变量的用法。

      创建变量

      如何在当前bash中创建一个变量呢,直接使用 "变量名=变量值"方式即可,比如

    [root@node1 ~]# lym=liuym
    [root@node1 ~]# echo $lym
    liuym
    [root@node1 ~]# 
    

       我们创建了一个变量,变量名叫lym,变量值为liuym

      引用变量

      我们已经创建了一个变量,那么怎么引用这个变量的值呢。

      引用变量:就是使用变量的意思,"${变量名}" 表示使用变量,示例如下

    [root@node1 ~]# lym=liuym
    [root@node1 ~]# echo ${lym}
    liuym
    [root@node1 ~]# 
    

       上图中,我们先在命令行中创建了一个变量,然后引用了它,可以看到,我们使用echo命令输出了变量值。

      大多数情况下,引用变量时,"${变量名}"中的"{ }""可以省略,但是某些情况下不行。

      我们先看看省略时是什么样子的。

    [root@node1 ~]# lym=liuym
    [root@node1 ~]# echo $lym
    liuym
    [root@node1 ~]# 
    

       上图中,使用echo输出变量名时,我们并没有使用"${变量名}"的方法引用变量值,而是省略了"{ }",使用"$变量名"的方式引用了变量值,但是,某些情况不能省略"{ }",哪种情况不能省略"{ }"呢,我来描述一下,比如,我们声明了一个animal变量,然后将animal变量的值设置为了pig,如下

    [root@node1 ~]# animal=pig
    [root@node1 ~]# echo $animal
    pig
    

       当我们引用animal变量的值的时候,其值为pig,而现在,我们想要使用echo命令,输出pigs ,那么,我们能不能直接在应用animal变量时直接加上s呢,我们试试。

    [root@node1 ~]# echo $animals
    
    [root@node1 ~]# 
    

       可以看到,这样是不行的,因为当我们这样引用变量时,系统会认为我们在引用一个新的变量"animals",如果想要达到我们的效果,在屏幕上输出pigs,我们则必须使用"${变量名}"的方式引用变量值,示例如下

    [root@node1 ~]# echo ${animal}s
    pigs
    [root@node1 ~]# 
    

       撤销变量

      如何撤销变量、释放变量呢。

      我们可以把撤销变量理解成删除一个变量,我们使用unset撤销变量,语法如下

      unset VARNAME

      撤销变量示例如下:

    [root@node1 ~]# lym=liuym
    [root@node1 ~]# echo $lym
    liuym
    [root@node1 ~]# unset lym
    [root@node1 ~]# echo $lym
    
    [root@node1 ~]# 
    

       可以看到,撤销变量后,再次使用echo命令输出变量值时,输出为"空",因为对应的变量已经不存在了。

      如果我们在脚本中定义了变量,当脚本执行完成后,脚本中的变量自动会被撤销,虽然说脚本在执行完成后,脚本中生成的变量会被释放,但是,最好还是养成及时撤销已使用的并且无再次使用可能的变量,及时撤销变量是良好的编程习惯,就像其他语言的开发中,关闭已经不再使用的数据库连接一样,如果某些脚本需要常驻后台执行,最好检查相应变量是否及时释放。

      以后我们还会用到函数,unset也可以撤销函数,当某个函数使用过后,不需要再次使用的时候,可以撤销此函数,使用如下语法撤销函数

      unset -f 函数名

      好了,创建变量、使用变量、撤销变量的操作我们已经说完了,那么我们说说变量的类型。

      bash是弱类型的语言,默认会把变量值当做字符串处理,所以,我们可以认为bash中的变量都是"字符串"。

      变量是存在作用域的概念的,按照作用域划分,一般可以划分为本地变量,环境变量,除了这两种变量,还有只读变量和一些特殊变量,那么,它们有什么不同呢,我们来总结一下。

      本地变量

      下图的方式其实就是在当前bash中创建一个本地变量,我们之前创建的变量,也是本地变量。

    root@node1 ~]# zsythink='www.zsythink.net'
    

       本地变量的作用域:本地变量只在当前bash进程中有效,对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效。

      本地变量中还可以细分为 局部变量 和 全局变量。

      在以后的总结中,我们还会学习到函数,可能会在函数中定义局部变量,局部变量创建方法如下。

      local varname=value

      局部变量的作用域:局部变量只对当前函数或代码段有效。

      如果脚本中存在全局变量var(注意:是脚本范围内的全局变量,不是bash命令行中) ,同时,脚本中某个函数中如果也有一个变量命名为var并且被赋值,那么函数中的var的值将会覆盖全局变量var的值,如果函数中声明var变量时,使用了local关键字,那么,函数中的local var的值 ,将不会覆盖全局变量var的值,而且在函数中使用的var变量的值仍是local var的值,函数外使用var变量的值仍是全局变量的var的值。

      那么我们用一段代码,演示上面的理论总结,如果你无法理解示例中的代码,没有关系,我们只是用它印证上面的话,不能理解就跳过此示例即可。

    [root@node1 shell_learn]# cat test.sh 
    #!/bin/bash
    #
    zsythink="www.zsythink.net"
    echo ${zsythink}
    
    testvarzone(){
    	zsythink="zsy's blog @ www.zsythink.net";
    	echo $zsythink
    }
    
    testvarzone
    echo $zsythink
    [root@node1 shell_learn]# sh test.sh 
    www.zsythink.net
    zsy's blog @ www.zsythink.net
    zsy's blog @ www.zsythink.net
    [root@node1 shell_learn]# cat test.sh 
    #!/bin/bash
    #
    zsythink="www.zsythink.net"
    echo ${zsythink}
    
    testvarzone(){
    	local zsythink="zsy's blog @ www.zsythink.net";
    	echo $zsythink
    }
    
    testvarzone
    echo $zsythink
    [root@node1 shell_learn]# sh test.sh 
    www.zsythink.net
    zsy's blog @ www.zsythink.net
    www.zsythink.net
    

     

       从上图中的实验可以看出,如果当zsythink变量并不是局部变量时,当我们第二次对zsythink变量赋值以后,相当于重新赋值了,而当一个变量被设置为局部变量时,因为局部变量的作用域只限制在当前函数内,所以,局部变量zsythink并不会覆盖脚本内的zsythink全局变量。这就是在脚本范围内,局部变量与全局变量的区别。

      当我们在当前shell执行一个脚本的时候,其实是在当前shell进程中生成了一个子进程,在子进程中运行的脚本。

      

      举个例子:

      我们直接在当前bash下运行test.sh脚本

    [root@node1 shell_learn]# cat test.sh 
    #!/bin/bash
    sleep 50
    [root@node1 shell_learn]# ./test.sh
    

       打开另一个终端,使用pstree命令查看进程树,发现如下图

       

      上图中蓝筐中的bash为刚才执行./test.sh命令的bash,当我们执行test.sh的时候,则生成一个子进程(红框中的test.sh),在子进程中运行test.sh

      如果我们在"蓝筐中的bash"中定义了本地变量,那么使用上述方式直接运行test.sh的时候,test.sh在运行时则不能够直接使用"蓝筐中bash"定义的本地变量,因为我们说过,本地变量只在在当前bash进程中有效,对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效,虽然红框中的test.sh进程为蓝筐中bash进程的子进程,但是仍然无法使用蓝筐中bash进程的本地变量,我们来做个试验。

      在当前bash中创建一个本地变量vtest,在当前bash中直接运行test.sh

    [root@node1 shell_learn]# zsythink="www.zsythink.net"
    [root@node1 shell_learn]# echo $zsythink
    www.zsythink.net
    [root@node1 shell_learn]# cat test.sh 
    #!/bin/bash
    echo ${zsythink}
    [root@node1 shell_learn]# ./test.sh 
    
    [root@node1 shell_learn]# 
    

     

       

      红色横线以上的部分表示我们在当前bash进程中输出了本地变量,而在test.sh脚本中,我们同样写了一条echo命令,输出zsythink变量,我们发现,test.sh并没有输出zsythink变量中的内容,因为test.sh实际上是在当前bash的子进程中运行,而zsythink是定义在当前bash中的本地变量,验证了前面的本地变量作用域的概念。

      如果想要test.sh脚本可以使用到当前bash中定义的变量,怎么办呢,有两种方式

      

      1、使用另一种不是本地变量的"变量",它被称作"环境变量",我们可以在当前bash中定义环境变量,环境变量的具体演示在后面,不要着急。

      2、在当前bash中,仍然使用"本地变量",但是改变执行test.sh脚本的方式。

      这里面牵扯到两个新概念"环境变量" 和  "执行脚本的方式",我们向下看,慢慢聊。

      环境变量

      环境变量这个名词搞IT的同学应该都比较熟悉,我就不啰嗦了

      环境变量的作用域:环境变量的生效范围为当前shell进程及其子进程

      

      使用export关键字指明对应的变量为环境变量,方法如下

      export varname=value

      也可以先声明为本地变量,然后再导出为环境变量,步骤如下

      zsythink="www.zsythink.net"

      export zsythink

      

      命令行中直接执行的shell脚本在执行时会启动一个子shell进程。

      命令行中直接执行的shell脚本会继承当前shell的环境变量

      系统自动执行的shell脚本(非命令行中执行)就需要自我定义需要的各种环境变量,或者导入一些已经存在的环境变量。

        我们已经知道,本地变量只能被当前shell进程使用,而环境变量可以被子进程使用到。

      我们还使用刚才在本地变量中使用过的例子,来套用到环境变量身上,看看,实际效果。

      我们直接在当前bash下执行test.sh脚本的时候,其实是在当前shell进程中生成了一个子进程,在子进程中运行的test.sh脚本。

       那么,我们在当前bash中定义一个环境变量,然后直接在当前bash中执行test.sh脚本,看看test.sh脚本能不能识别到,继承到环境变量。

    [root@node1 shell_learn]# export zsythink="www.zsythink.net"
    [root@node1 shell_learn]# cat test.sh 
    #!/bin/bash
    echo ${zsythink}
    [root@node1 shell_learn]# ./test.sh 
    www.zsythink.net
    [root@node1 shell_learn]# 
    

       我们发现,test.sh输出了zsythink变量中的内容,因为test.sh实际上是在当前bash的子进程中运行,而zsythink是定义在当前bash中的环境变量,验证了环境变量可以被继承的概念。

         有的人就是记性好,你是不是还记得,我们说过,想要让test.sh可以使用到当前bash中定义的变量,除了定义环境变量这种方法,还有另一种方法,就是改变执行test.sh脚本的方式,这句话是什么意思,怎么做,我们向下看。

        告诉你一个秘密,在当前bash中,除了使用"路径+名称脚本"的方式直接运行脚本以外,还能使用另一种方法运行脚本,就是使用source关键字,我们只要在原有的方法前面加入source关键字即可,如下图。

    [root@node1 shell_learn]# cat test.sh 
    #!/bin/bash
    echo "www.zsythink.net"
    sleep 50
    [root@node1 shell_learn]# source ./test.sh 
    www.zsythink.net
    

       细心的你已经发现,脚本已经执行了,而且按照我们的指令,睡眠50秒,趁着他睡眠的功夫,我们打开另一个终端,查看一下当前系统中的进程树

       我们发现,这样执行脚本,似乎和不加source直接执行脚本时显示的进程不太一样。

      

      我们来对比一下:

      加source关键字的时候,执行脚本时,进程树如下

       不加source关键字,直接运行脚本的时候,进程树如下,

       细心的你又发现了,不加source关键字的时候,执行脚本,相当于创建了一个子进程,然后在子进程中运行脚本,如果加了source关键字,相当于没有创建子进程,而是直接在当前bash中运行了脚本。

      

      我似乎想到了什么·····

      本地变量只能在当前shell进程中生效,而当我们在执行脚本的时候如果加入了source关键字,那么脚本就相当于在当前进程中执行,而不是在子进程中执行,那么,如果使用source的方式执行脚本,理论上来说,本地变量是可以被脚本使用的,因为它们都在一个进程中,我们来验证一下。

    [root@node1 shell_learn]# zsy="www.zsythink.net"
    [root@node1 shell_learn]# cat test.sh 
    #!/bin/bash
    echo "$zsy";
    [root@node1 shell_learn]# ./test.sh 
    
    [root@node1 shell_learn]# source test.sh 
    www.zsythink.net
    [root@node1 shell_learn]# 
    

       如上图,使用两种方式执行脚本,得到的结果不一样,验证了我们的想法。

      

      此处还要说明一点source /some/file相当于 ". /some/file"

      ". /some/file"用文字描述就是"点 空格 脚本路径",这样说是为了让你区分出如下两种写法:

      "./some/file"  和  ". /some/file"

      他们长得非常像,所以此处向新手同学强调一下。 只是一个有空格一个没有空格

      其实 source /some/file 可以理解为将/some/file文件中的内容包含到当前文件或者进程中。

      "."的作用和source的作用相同。

       需要注意一点,先看示例,如下。

    [root@node1 shell_learn]# export zsy="zsy"
    [root@node1 shell_learn]# zsy="www.zsythink.net"
    [root@node1 shell_learn]# echo $zsy
    www.zsythink.net
    [root@node1 shell_learn]# 
    

       如上写法表示声明一个环境变量,然后重新对这个环境变量赋值,并不能表示它从一个环境变量变成了本地变量,如果想把它从本地变量变成环境变量,需要先unset,然后再重新声明为本地变量并赋值。

      只读变量

      只读变量,顾名思义,就是设置了以后只能读不能改。

      可以把只读变量理解成一个常量。

       使用如下方法设置一个只读变量

      readonly rovarname=value

      例如

      readonly pai=3.1415926

      只读变量设置后不可修改,不可unset(撤销),如果想要只读变量失效需要退出当前shell。

    [root@node1 shell_learn]# readonly zsy="www.zsythink.net"
    [root@node1 shell_learn]# unset zsy
    -bash: unset: zsy: 无法反设定: 只读 variable
    [root@node1 shell_learn]# 
    

       只读变量的只对当前bash生效,"子bash"无法继承当前bash的只读变量,如果想要当前bash的"子bash"或者"孙bash"也能继承当前bash的只读变量,那么需要将只读变量变成环境变量的一种,变成"环境只读变量",使用如下方法,声明一个"环境只读变量"

      export readonly varname=value

      "环境只读变量"可以被子bash继承到后直接使用。

      特殊变量

      特殊变量往往在脚本中使用,我们会在以后的脚本示例总结中对它们演示,此处只是总结出来,方便有经验的朋友回忆,看不懂没关系,我们会在以后的应用中应用到它们,到时候自然会明白。

      特殊变量:$?

      上述特殊变量保存了上一个命令的执行状态返回值。

      命令执行后,可能有两类状态返回值(0 - 255)

      如果返回值为0:表示上一条命令正确执行

      如果返回值为1-255中的任意一个:表示上一条命令执行错误

      1到255中,1、2、127 为系统预留的错误状态码,其他状态码可自定义。

      

      位置变量

      脚本中的特殊变量

      $# 表示传入脚本的参数个数,参数数量

      $*参数列表 同 $@

      $@参数列表,获取到所有参数

      ${@:起点}  表示由起点开始(包括起点),取得后面的所有的位置参数

      ${@:起点:个数} 表示由起点开始(包括起点),取得指定个数的位置参数

      当我们直接使用$*和$@的时候,这两种写法没有任何区别,

      但是,在对$* 和$@加引号后,变成了"$*"和"$@", 这两种写法就会产生区别

      $@ $* 只在被双引号包起来的时候才会有差异

      "$*": 传递给脚本的所有参数,全部参数合为一个字符串

      "$@": 传递给脚本的所有参数,每个参数为独立字符串

      $0表示脚本本身,相当于basename输出的内容

       

      使用shift 可剔除1个参数

      shift -n 一次同时剔除最前面的n个参数,踢出头后面的参数自动向前排

      shift表示上档

      如下特殊变量用来在脚本内引用传入脚本的参数值。

      $1 , $2 ……



      使用shift 可剔除1个参数

      shift -n 一次同时剔除最前面的n个参数,踢出头后面的参数自动向前排

      shift表示上档

       Shift 命令还有另外一个重要用途, Bash 定义了9个位置变量,从 $1 到 $9,这并不意味着用户在命令行只能使用9个参数,借助 shift 命令可以访问多于9个的参数。Shift 命令一次移动参数的个数由其所带的参数指定。例如当 shell 程序处理完前九个命令行参数后,可以使用 shift 9 命令把 $10 移到 $1 。

      如果直接使用shift 不加任何数字,那么代表最前面的$1被剔出,$1被踢出后,当前的$2变成了$1,我们可以通过这种方法,每次只处理第一个所谓的"$1"。

       如果你直接使用 $25 那么不会输出第25个参数,因为一共就9个参数位,如果直接输入$25,会输出第2个参数的内容后多出一个5,假如$2的值是a,那么,直接使用$25会变成a5。

      告诉你一个秘密,使用如下写法不用上档

       

      位置变量使用${}的方式表示,不用上档。

      位置变量不能被继承 ,只有环境变量可以被继承。

      其他创建变量的方法

      也可以使用declare声明一个变量

      declare abc=123

      -i 表示为声明的变量为整形,此处说的整形与前面提到的弱类型并不矛盾,它本质仍然是字符串,只是方便我们以后把它当做数字,对它进行运算。

      declare -i sum=0

      -x 表示为声明的变量为环境变量

      declare -x expvarname=

      declare -x expvarname=  这样的写法相当于   export expvarname=

      declare -r name 声明一个只读变量

      这些选项可以混合使用,比如声明一个可以被继承的"环境只读变量"

      declare -rx zsy="www.zsythink.net"

  • 相关阅读:
    一名菜鸟程序员的跳槽经历以及其所感所想(二)
    C#调用WebService
    IIS Error:404.2 The page you are requesting cannot be served because of the ISAPI and CGI Restriction list settings on the Web server
    C#操作XML简析系列(1)之增删修改查
    The web server process that was being debugged has been terminated by Internet Information Services (IIS).
    一名菜鸟程序员的跳槽经历以及其所感所想(一)
    访问WebService出现IIS错误:The request failed with HTTP status 401: Unauthorized
    Windows2008服务器搭建Apollo_MQTT服务
    [ObjC笔记] "self = [super init]"的解释与潜藏bug
    [LBS]查询离某个经纬附近的数据SQL语句
  • 原文地址:https://www.cnblogs.com/minseo/p/13716634.html
Copyright © 2011-2022 走看看