• 二、Shell变量


    类型             注释
    强变量 变量在使用前,必须事先声明,甚至还需要初始化 弱变量 变量用时声明,甚至不区分类型

    变量的作用:用来保存变化的数据

    变量名    名称固定,由系统设定或用户定义
    变量值    根据用户设置、系统环境变化而变化

    设置方法

    变量名=变量值

    Shell变量的类型

    类型                     说明
    环境变量(全局变量)    由系统维护,用于设置工作环境,只有个别环境变量用户可以直接更改。可以在创建它们的shell及其派生出来的任意子进程shell中使用。
    位置变量            通过命令行给脚本程序传递参数
    预定义变量          bash内置的一类有特殊用途的变量,可以直接调用,但不能直接赋值或修改
    自定义变量          由用户自己设置、修改及使用

    环境变量

    在系统启动时,加载系统配置文件定义的变量
    变量名和变量值是系统设置
    变量名通常用大写字母定义
    变量存的值是规定的,通常情况下不建议修改系统环境变量的值
    作用域为当前shell进程及其子进程

    环境变量用于定义shell的运行环境,保证shell命令的正确执行,shell通过环境变量来确定登陆用户名、命令路径、终端类型、登陆目录等,所有的环境变量都是系统全局变量,可用于所有子进程中,这包括编辑器、shell脚本和各类应用。

      环境变量可以在命令行中设置,但用户退出时这些变量值也会丢失,因此最好在用户家目录下的.bash_profile文件中或全局配置/etc/bashrc,/etc/profile文件或者/etc/profile.d/中定义。将环境变量放入profile文件中,每次用户登录时这些变量值都会被初始化。

      传统上,所有环境变量均为大写。环境变量应用于用户进程前,必须用export命令导出。

      环境变量可用在创建他们的shell和从该shell派生的任意子shell或进程中。他们通常被称为全局变量以区别局部变量。通常,环境变量应该大写。环境变量是已经用export内置命令导出的变量。

      有些环境变量,比如HOME、PATH、SHELL、UID、USESR等,在用户登陆之前就已经被/bin/login程序设置好了。通常环境变量定义并保存在用户家目录下的.bash_profile文件中。

    配置文件

     

    配置文件        注释
    /etc/profile    
    ~/.bashrc_profile    

    相关操作

    env    列出所有的环境变量
    set    列出所有变量

    常见的环境变量

       
    PWD    
    PATH    
    USER    
    LOGNAME    
    UID    
    SHELL    
    HOME    
    PS1    
    PS2    

    如果想设置环境变量,就要在给变量赋值之后或设置变量时使用export命令。带-x选项的declare内置命令也可完成同样的功能。

    (注意:输出变量时不要在变量名前面加$)

    ①export 变量名=value
    
    ②变量名=value;export 变量名
    
    ③declare -x 变量名=value

    提示:以上为三种设置环境变量的方法

    例:

    export NAME = zgy
    
    declare -x NAME =zgy
    
    NAME=zgy;export NAME

    自定义全局环境变量实例:

    [zgy@Web ~]$ . .bash_profile
    
    [zgy@Web ~]$ echo $ZGY
    
    zgy
    
    [root@Web ~]# cat /etc/profile | grep ZGY
    
    export ZGY='zgy'
    
    [root@Web ~]# source /etc/profile 或. /etc/profile使其生效
    
    [root@Web ~]# echo $ZGY
    
    zgy
    
    [root@Web ~]# env | grep ZGY
    
    ZGY=zgy
    
     

    自定义环境变量生产环境java环境配置实例:tomcat,resin,csvn,hadoop.

    export JAVA_HOME=/application/jdk
    
    export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib
    
    export RESIN_HOME=/application/resin

    常见放在:/etc/profile

    如何查看变量值

    echo $变量名

    位置变量:只读变量

    位置变量        只读变量
                在执行脚本或Shell时,给脚本或函数传值的;表示为$n,n为正整数
    $1.....$n      
    ${10}......${N}    

    预定义变量(特殊变量)

    当前脚本使用的Shell里面预先定义好的变量
    变量名是固定的,变量值通常不修改

    位置变量

    $0:获取当前执行的shell脚本的文件名,包括脚本路径。
    $n:获取当前执行的shell脚本的第n个参数值,n=1..9,当n为0时表示脚本的文件名,如果n大于9,用大括号括起来,例如${10}
    $*:获取当前shell的所有参数,将所有的命令行参数视为单个字符串,相当于”$1$2$3”...注意与$#的区别
    $#:获取当前shell命令行中参数的总个数
    $@:这个程序的所有参数”$1””$2””$3””...”,这是将参数传递给其他程序的最佳方式,因为他会保留所有内嵌在每个参数里的任何空白。
    提示:$*和$@的区别?

    进程状态变量

    $$:获取当前shell的进程号(PID)
    $!:执行上一个指令的PID
    $?:获取执行上一个指令的返回值(0为成功,非零为失败)  #这个变量很常用
    $_:在此之前执行的命令或脚本的最后一个参数

     举例:

    [root@Web ~]# cat etiantian.sh 
    echo '$0  获取当前执行的shell脚本的文件名,包括脚本路径:' $0
    echo '$n 获取当前执行的shell脚本的第n个参数值,n=1..9:' '$1'=$1 '$2'=$2 "$3=$3"   #当使用双引号时必须把两边括起来,并且要用反斜杠
    echo '$* 获取当前shell的所有参数 "$1 $2 $3...注意与$#的区别:'$*
    echo '$#:获取当前shell命令行中惨呼的总个数:' $#
    echo '$@:这个程序的所有参数”$1””$2””$3””...”:' $@
    echo '$$ 获取当前shell的进程号(PID):'$$
    sleep 2
    echo '$!:执行上一个指令的PID:' $!
    echo '$? 获取执行上一个指令的返回值:' $?
    echo '$_:在此之前执行的命令或脚本的最后一个参数:' $_

    执行结果:

    [root@Web ~]# sh etiantian.sh 参数1 参数2 参数3
    'etiantian.sh 获取当前执行的shell脚本的文件名,包括脚本路径:' etiantian.sh
    $n 获取当前执行的shell脚本的第n个参数值,n=1..9: $1=参数1 $2=参数2 $3=参数3
    $* 获取当前shell的所有参数 "$1 $2 $3...注意与$#的区别:参数1 参数2 参数3
    $#:获取当前shell命令行中参数的总个数: 3
    $@:这个程序的所有参数”$1””$2””$3””...”: 参数1 参数2 参数3
    $$ 获取当前shell的进程号(PID):5738
    $!:执行上一个指令的PID:
    $? 获取执行上一个指令的返回值: 0
    $_:在此之前执行的命令或脚本的最后一个参数: 0

    $n实例:

    (1)$1,$2....$9${10}${11}

    (1)$1,$2....$9${10}${11}
    [root@Web ~]# cat n.sh 
    echo $1
    [root@Web ~]# sh  n.sh boy zhaoyang
    boy
    [root@Web ~]# sh n.sh "boy zhaoyang"
    boy zhaoyang

    提示:加引号括起来表示为一个参数

    [root@Web ~]# cat n.sh 
    echo $1 $2
    [root@Web ~]# sh n.sh boy zhanyang
    boy zhanyang
    [root@Web ~]# sh n.sh "boy zhanyang" luya
    boy zhanyang luya

    (2)$0取脚本的名称,包括路径

    [root@Web ~]# cat n.sh 
    echo $0 
    [root@Web ~]# sh n.sh 
    n.sh

    提示:当要执行的脚本为全路径时$0也会带着路径。此时如果希望取出名称或路径,用下面的方法。

    [root@Web ~]# cat dirname.sh 
    dirname $0
    [root@Web ~]# sh dirname.sh 
    .
    [root@Web ~]# basename /etc/inittab 
    inittab
    [root@Web ~]# cat 0.sh
    dirname $0
    basename $0
    [root@Web ~]# sh 0.sh
    .
    0.sh

    $?实例

    [root@Web ~]# su - zgy
    [zgy@Web ~]$ ll
    总用量 8
    -rwxrw-r--. 1 zgy zgy 14 4月  11 16:06 test.sh
    -rw-rw-r--. 1 zgy zgy 14 4月  11 13:12 testsource.sh
    [zgy@Web ~]$ echo $?
    0
    [zgy@Web ~]$ ll /root/
    ls: 无法打开目录/root/: 权限不够
    [zgy@Web ~]$ echo $?
    2
    [zgy@Web ~]$ ddd
    -bash: ddd: command not found
    [zgy@Web ~]$ echo $?
    127
    1)编译软件的时候判断成功与否
    make 
    make install
    echo $?

    练习:写一个脚本

    给脚本传递两个参数(整数)

    显示此两者之和,之乘积

    #!/bin/bash
    #
    if [ $# -lt 2 ]; then
      echo "Usage: cacl.sh ARG1 ARG2"
      exit 8
    fi
    
    echo "The sum is: $[$1+$2]."
    echo "The prod is: $[$1*$2]."

    退出状态及其含义:

     $?返回值参考:

    退出状态及其含义    注释
    0          表示运行成功,程序执行未遇到任何问题
    1~125        表示运行失败,脚本命令、系统命令错误或参数传递错误
    126         找到该命令但无法执行
    127         未找到要运行的命令
    >128         命令被系统强行结束
    12127       系统预留       

     提示:在脚本调用,一般用exit 0,函数return 0.

     解析:

    0:表示运行成功
    
    2:权限拒绝
    
    1~125:表示运行失败,脚本命令、系统命令错误或参数传递错误
    
    126:找到该命令了,但是无法执行
    
    127:未找到要运行的命令
    
    >128:命令被系统强制结束

    提示:在脚本调用,一般用exit 0,函数return 0.

    难点理论与实战剖析

    $*和$@的区别
    $*: 获取当前shell的所有参数将所有的命令行所有参数视为单个字符串,等同于"$1$2$3"
    $@:将命令行每个参数视为单独的字符串,等同于"$1""$2""$3"。这是将参数传递给其他程序的最佳方式,
    因为它会保留所有内嵌在每个参数里的任何空白。

     获取当前shell的所有参数,将所有的命令行参数视为单个字符

    示例1:

    [root@Web ~]# set -- "I am" handsome boy  #传入三个参数
    [root@Web ~]# echo $#
    3
    [root@Web ~]# for i in $*;do echo $i;done  #循环打印这些参数,用$*,无引号
    I
    am
    handsome
    boy

    示例2:

    [root@Web ~]# for i in $@;do echo $i;done  #在没有双引号的情况下,这里的结果$*和$@是一样的
    I
    am
    handsome
    boy
    [root@Web ~]# for i;do echo $i;done  #去掉in变量列表,相当于in “$@”
    I am
    handsome
    boy
    
    [root@Web ~]# for i in "$@";do echo $i;done  #在有双引号的情况下,参数里引号内容当作一个参数输出了,这才符合我们真正的参数需求,set -- "I am" handsome oldboy.
    I am
    handsome
    boy
    [root@Web ~]# for i in "$*";do echo $i;done   #加了双引号,$*表示一个字符串
    I am handsome boy
    [root@Web ~]# shift   #用shift去掉第一个参数
    [root@Web ~]# echo $#
    2
    [root@Web ~]# for i in "$*";do echo $i;done  #再次打印只剩下后面两个参数了
    handsome boy
    [root@Web ~]# for i in "$@";do echo $i;done
    handsome
    boy

    其他:https://blog.51cto.com/oldboy/1175971

    linux下set和eval的使用小案例精彩解答

    linux下set和eval的使用小案例解答
    本博文主要是讲解学生提出的如下一行命令脚本定义的真正内涵:
    runlevel=$(set -- $(runlevel); eval "echo $$#" )
    以下是老男孩培训14期VIP群内的对话解答记录,内容已做过滤,有的网友以前反映过,说老男孩发的记录时间对不上,其实老男孩只是以对话问答的形式让大家掌握技术而已,内容是加工过的属正常,不要大惊小怪,给大家看的一定是最容易理解的对话过程,也尽量保持了原来的对话。
    解答对话:
    北京-刘同学(123437415) 2013/4/10 23:01:07
    求问一些服务的启动脚本runlevel=$(set -- $(runlevel); eval "echo $$#" ) 这个runlevel是怎么把这个运行级别的取到的
     
    北京老男孩老师(49000448) 10:57:44
    你敲一下runlevel看看。
     
    北京-刘同学(123437415) 10:59:39
    [root@G307 ~]# runlevel
    N 3
     
    北京老男孩老师(49000448) 11:00:31
    后面的3就是运行级别
    
    北京-刘同学(123437415) 11:00:39
    啊,明白了,他是 $#=2 然后 $2=3是这样吧
    老男孩老师点评:对。
    
    北京-陶同学<DD@sina.com> 11:05:19
    $# 获取当前shell命令行中参数的总个数
    $n 获取当前执行的shell脚本的第n个参数值,n=1,...9,当n为0时表示脚本的文件名,如果n大于9,用大括号括起来${10}
    老男孩老师点评:这个同学说的是命令行脚本传参的内容,也适合本文。
    
    北京-李灿(123458724) 11:08:22
    eval对echo $$#的双重扫描
    假如要的参数不是最后一个 那么这个总数 就不准确了是吧
    老男孩老师点评:对的。取最后一个才能这么用。
    
    北京老男孩老师(49000448) 11:09:19
    set 和eval的用法比较特殊,后期课程慢慢的给大家渗透,现在是学习的刚刚开始啊。
    先给大家一个例子:
    [root@oldboy ~]# set -- "I am" handsome oldboy. #==>传入三个参数
    [root@oldboy ~]#
    [root@oldboy ~]# echo $#    #==>现在有三个参数
    3
    [root@oldboy ~]# for i in $*;do echo $i;done #==>循环打印这些参数,用$*,无引号
    I
    am
    handsome
    oldboy.
    [root@oldboy ~]# for i in $@;do echo $i;done #==>在没有双引号的情况下,这里的结果$*和$@是一样的
    I
    am
    handsome
    oldboy.
     
    北京老男孩老师(49000448) 11:10:22
    上面是咱们老男孩培训后期课程教案里的一个例子
     
    北京老男孩老师(49000448) 11:12:59
    [root@oldboy ~]# set -- $(runlevel)
    [root@oldboy ~]# echo $#
    2
    [root@oldboy ~]# echo $(set $(runlevel); eval "echo $2" )
    3
    刘同学同学你理解这个吧。这个会了,那个就差不多了
     
    北京-刘同学(123437415) 11:13:45
    恩,懂了
     
    北京老男孩老师(49000448) 11:13:55
    set可以像命令行一样设置传参,这个用法不多见!
    了解就得,工作几乎不需要必须用它
     
    北京-刘同学(123437415) 11:14:20
    恩,好的
     
    北京老男孩老师(49000448) 11:15:42
    既然你懂了,我就在多说下,哈哈!
    runlevel=$(set -- $(runlevel); eval "echo $$#" )
    1)这里的$#就是参数个数为2,即runlevel的结果, 所以$$#就是$2,即runlevel结果的第二列3
    [root@G307 ~]# runlevel
    N 3
    2)eval就是把echo的字符串,当做命令解析!解答了这么多 其实知识含量已经非常多了。
    可以写一篇博文了。
     
    北京-李灿(123458724) 11:16:52
    echo $ #输出括号内的值
    (set $(runlevel); #将runlevel参数值,传入shell,N 3
     eval "echo $2" ) #eval二次扫描,首先扫描到echo输出$2,第二次解析$2的值为等级3
    这样子吗?

    bash内部变量

    有些内部命令在目录列表时是看不见的,它们由shell本身提供,常见的内部命令有:

    echo,eval,exec.export,readonly,read,shift,wait,exit和点(.)

    echo 变量名表

     

    将变量名表指定的变量显示到标准输出

    evalargs

     

    读入参数args,并将它们组合成一个新的命令,然后执行

    exec 命令参数

    当shell执行带exec语句时,不会去创建新的子进程,而是转去执行指定的命令,当指定的命令执行完后时,该进程(也就是最初的shell)就终止了,所以shell程序中exec后面的语句将不再被执行

     

    export变量名=value

    shell可以用export把它的便利向下带入子shell,从而让子进程继承父进程中的环境变量。但子shell不能用export把它的变量向上带入父shell。

    readonly 变量名

     

    只读变量 用readonly显示所有只读变量

    read 变量名表

    从标准输入读字符串,传给指定变量
    
    可以在函数中用local变量名的方式申请局部变量

    shift 语句

    Shift语句按如下方式重命名所有的位置参数变量,即$2成为$1,$3称为$2...在程序中每使用一个shift语句,都使所以的位置参数一次向左移动一个位置,并使位置参数$#减1,直到减到0为止。

    内部变量实践:

    1)shift命令实例
    
    [root@Web ~]# cat n.sh
    
    echo $1 $2
    
    if [ $# -eq 2 ];then
    
        shift
    
    echo $1
    
    fi
    
    [root@Web ~]# sh n.sh 1 2
    
    1 2      #这里是echo $1 $2的结果
    
    2        #这里是echo $1的结果但是是传参时$2的值

    查看变量值和撤销变量

    显示变量值

    通过echo命令打印环境变量

    echo $变量名或echo ${变量名}
    $:通过指定变量名称引用变量值

    例子:

    $HOME:用户登录时进入的目录
     $UID:当前用户的UID(用户标识)相当于id -u
     $PWD:当前工作目录的绝对路径名
     $SHELL:当前SHELL
     $USER:当前用户
    .....省略若干

    例子2:

    [root@Web ~]# echo $USER
    root
    [root@Web ~]# echo $PWD
    /root
    [root@Web ~]# echo $UID
    0
    [root@Web ~]# echo $SHELL
    /bin/bash

     提示:在写shell脚本时可以直接使用上面的系统默认的环境变量。

    env(printenv)或set显示默认的环境变量

    set | grep 变量名
    env | grep 变量名   看不到自定义变量

    unset消除本地变量和环境变量

    unset 变量名

    例子:

    [zgy@Web ~]$ echo $USER
    zgy
    [zgy@Web ~]$ unset USER
    [zgy@Web ~]$ echo $USER
                    #此处为输出的空行

    可以看到变量的内容显示为空了

     本地变量在用户当前的shell生存期的脚本中使用。例如,本地变量OLDBOY取值为ett098,这个值只在用户当前shell生存期中有意义。如果在shell中启动另一个进程或退出,本地变量OLDBOY值将无效。

    变量名的设置规则

    由数字/字母/下划线组成,区分大小写
    不能以数字开头
    等号两边不要有空格
    不要使用关键字和特殊字符
    多次给同一个变量赋值时,最后的值生效

    普通字符串变量定义

    变量名=value
    
    变量名='value'
    
    变量名="value"

     例1:下面的例子会输出什么结果。

    a=192.168.1.2
    b='192.168.1.2'
    c="192.168.1.2"
    echo "a=$a"
    echo "b=$b"
    echo "c=${c}"

    提示:

    1)$c和${c}在这里等同

    2)需要在命令行实践以上内容

     

    思考:想一想a,b,c各是什么结果

    答案:

    [zgy@Web ~]$ echo "a=$a"
    a=192.168.1.2
    [zgy@Web ~]$ echo "b=$b"
    b=192.168.1.2
    [zgy@Web ~]$ echo "c=${c}"
    c=192.168.1.2

    例2:想一想a,b,c各是什么结果?

    a=192.168.1.2-$a
    b='192.168.1.2-$a'
    c="192.168.1.2-$a"
    echo "a=$a"
    echo "b=$b"
    echo "c=${c}"

    思考:再想一想a,b,c现在各是什么结果?为什么是这个结果?

    zgy@Web ~]$ echo "a=$a"
    a=192.168.1.2-192.168.1.2
    [zgy@Web ~]$ echo "b=$b"
    b=192.168.1.2-$a
    [zgy@Web ~]$ echo "c=${c}"
    c=192.168.1.2-192.168.1.2-192.168.1.2

    提示:

    1.第一种定义a变量的方式是直接定义变量内容,内容一般为简单连续的数字、字符串、路径名等。
    2.第二种定义b变量的方式是通过单引号定义变量。这个方式的特点是:输出变量时引号里是什么就输出什么,即使内容中有变量也会把变量名原样输出。此法比较适合于定义显示纯字符串
    3.第三种定义c变量方式是通过双引号定义变量。这个方式的特点是:输出变量时引号里的变量会经过解析后输出该变量内容,而不是把引号中变量名原样输出,适合于字符串中附带有变量的内容的定义。
    习惯:数字不加引号,其他默认加双引号。

    定义变量单引号、双引号与不加引号

    有关单引号、双引号与不加引号的简要说明如下:

    单引号:
    可以说是所见即所得:即将单引号内的内容原样输出,或者描述为单引号里面看到的是什么就会输出什么。
    双引号:
    把双引号内的内容输出来;如果内容中有命令、变量等,会先把变量、命令解析出结果,然后再输出最终结果。
    无引号:
    把内容输出来,会将含有空格的字符串视为一个整体输出,如果内容中有命令、变量等,会先把变量、命令解析出结果,然后再输出最终结果来,
    如果字符串中带有空格等特殊字符,则不能完整的输出,需要改加双引号,一般连续的字符串,数字,路径等可以不加任何引号,不过最好用双引号替代之。

    单引号、双引号与不加引号实践演示:

    范例1:经过反引号的`date`命令测试

    [root@localhost ~]# echo '`date`'   #单引号时看到什么就显示什么
    `date`
    [root@localhost ~]# echo "`date`"   #双引号时如果里面是变量,会先把变量解析成具体内容在显示
    2015年 04月 11日 星期六 22:08:16 CST
    
    [root@localhost ~]# echo `date`   #对于连续的字符串等内容一般不加引号也可,加双引号一般比较保险,推荐
    2015年 04月 11日 星期六 22:08:46 CST

    范例2:变量定义后,调用时测试

    [root@localhost ~]# ZGY=testchars  #创建一个不带引号的变量
    [root@localhost ~]# echo $ZGY  #不加引号,显示一个变量解析的内容
    testchars
    [root@localhost ~]# echo '$ZGY'  #单引号,显示一个变量本身
    $ZGY
    [root@localhost ~]# echo "$ZGY"   #双引号,显示一个变量内容,引号内可以是变量、字符串等
    testchars

    范例3:grep过滤字符串例子

    [root@localhost ~]# cat >grep.log
    testchars
    zgy
    [root@localhost ~]# grep "$ZGY" grep.log 
    testchars
    [root@localhost ~]# grep $ZGY grep.log 
    testchars
    [root@localhost ~]# grep '$ZGY' grep.log 
    过滤双引号测试
    [root@localhost ~]# cat >grep.log
    "
    [root@localhost ~]# grep '"' grep.log 
    "

    特殊例子:awk调用shell变量引号例子

    [root@localhost ~]# ETT=123
    [root@localhost ~]# awk 'BEGIN{print '$ETT'}'
    123
    [root@localhost ~]# awk 'BEGIN {print "$ETT"}'
    $ETT

    提示:以上的结果正好的前面的结论相反。这是awk调用shell变量的特殊用法

    [root@localhost ~]# ETT='abc'
    [root@localhost ~]# awk 'BEGIN {print "$ETT"}'
    $ETT
    [root@localhost ~]# awk 'BEGIN {print '$ETT'}'
    
    [root@localhost ~]# awk 'BEGIN {print "'$ETT'"}'
    abc

    提示:

    这个例子更特殊了一点。

    有关awk调用shell变量读者还可以参考http://blog.51cto.com/oldboy/760192

     一道实用linux运维问题的9种shell解答方法

    问题为:

    4)已知:/etc/hosts的内容为
    192.168.1.11  oldboy11.etiantian.org
    192.168.1.21  oldboy21.etiantian.org
    192.168.1.31  oldboy31.etiantian.org
    #192.168.1.111  oldboy111.etiantian.org
    请用shell脚本实现,怎么才能在输入IP后找到/etc/hosts里对应的唯一的hostname?

    解答:

    法1)脚本过滤法
    
    [root@old_boy scripts]# cat judgehost.sh   
    #!/bin/bash  
    echo "please input ip address:" 
    read ip  
    [ -n "`grep "$ip " /etc/hosts`" ] &&   #注意前面的过滤条件结尾带有空格。  
    echo "The hostname is: `grep "$ip " /etc/hosts |awk '{print $2}'`" ||   
    echo "The ip is invalid" 
    
     提示:
    1)这是一个grep过滤加条件判断的实现语法:
    2)条件判断语法为[ -n "ddd" ] && echo 1 || echo 0
    3)[ -n "`grep "$ip " /etc/hosts`" ] &&   #注意前面的过滤条件结尾带有空格。这里啊,是为了排除下面的重复情况
     192.168.1.11  oldboy11.etiantian.org
     192.168.1.111  oldboy111.etiantian.org
    ----------------我是每种方法分隔符---------------
    法2)脚本精确匹配法:
    
    #!/bin/bash  
    #author oldboy  
    #qq 31333741  
    #judge input  
    if [ $# -ne 1 ]  
      then 
        echo "input error!" 
        exit 1  
    fi  
     
    flag=0  
    exec < /etc/hosts  
    while read line  
    do  
     if [ "$1" = "`echo $line|awk '{print $1}'`" ]  
       then 
           flag=1  
           echo "the $1 's hostname is `echo $line|awk '{print $2}'`"   
           break;  
     fi  
    done   
    [ $flag -eq 0 ] && echo " sorrry,not find $1 's hostname!" 
         
    
    提示:此题,请大家学习while的用法及设置flag的思路。
    执行结果:
    [root@old_boy scripts]# sh oldboy.sh 192.168.1.11
    the 192.168.1.11 's hostname is oldboy11.etiantian.org
    [root@old_boy scripts]# sh oldboy.sh 192.168.1.21
    the 192.168.1.21 's hostname is oldboy21.etiantian.org
    [root@old_boy scripts]# sh oldboy.sh 192.168.1.311
     sorrry,not find 192.168.1.311 's hostname!
    ----------------我是每种方法分隔符---------------
     
    
    特别提示:下面的方法中,老男孩老师大量的使用了awk的不同方法来实现同样的功能,来告诉大家,awk是很强大的, 希望同学们能按照老师的教学要求精通之。
    
    法3)awk精确匹配:
    准备:
    [root@old_boy scripts]# tail -4 /etc/hosts
    192.168.1.11  oldboy11.etiantian.org
    192.168.1.111  oldboy111.etiantian.org
    192.168.1.21  oldboy21.etiantian.org
    192.168.1.31  oldboy31.etiantian.org
    脚本:
    
    [root@old_boy scripts]# cat awkhost1.sh   
    awk 'BEGIN {a="'$1'"} {if($1==a) print $2; }' /etc/hosts  
    
    执行结果:
    [root@old_boy scripts]# sh awkhost1.sh 192.168.1.21
    oldboy21.etiantian.org
    [root@old_boy scripts]# sh awkhost1.sh 192.168.1.31
    oldboy31.etiantian.org
    [root@old_boy scripts]# sh awkhost1.sh 192.168.1.11
    oldboy11.etiantian.org
    提示:注意a="'$1'"的用法,$1为命令行传参。awk程序中调用系统变量的方法a="'$1'"----------------我是每种方法分隔符---------------
    法4)awk精确匹配法
    
    [root@old_boy scripts]# cat awkhost2.sh   
    awk '{if($1=="'$1'") print $2}' /etc/hosts  
    
    执行结果:
    [root@old_boy scripts]# awkhost2.sh 192.168.1.11
    oldboy11.etiantian.org
    [root@old_boy scripts]# awkhost2.sh 192.168.1.21
    oldboy21.etiantian.org
    [root@old_boy scripts]# awkhost2.sh 192.168.1.311
    ----------------我是每种方法分隔符---------------
    法5)awk过滤法
    
    [root@old_boy scripts]# cat awkhost4.sh   
    awk '/'"${1} "'/''{print $2}' /etc/hosts  
    执行结果:  
    [root@old_boy scripts]# awkhost4.sh 192.168.1.21  
    oldboy21.etiantian.org  
    [root@old_boy scripts]# awkhost4.sh 192.168.1.11  
    oldboy11.etiantian.org  
    [root@old_boy scripts]# awkhost4.sh 192.168.1.31  
    oldboy31.etiantian.org  
    提示:除了语法外,这道题有个学问,就是过滤时传参结尾要带个空格,这样才能过滤重复IP的情况  
    如:  
     192.168.1.11  oldboy11.etiantian.org  
     192.168.1.111  oldboy111.etiantian.org 
    
    ----------------我是每种方法分隔符---------------
    法6)awk过滤法
    
    [root@old_boy scripts]# cat awkhost5.sh   
    awk '{if($1~/'$1'/) print $2}'  /etc/hosts ##如果文件第一列包含命令行第一个参数字符则打印第二列  
    执行结果:  
    [root@old_boy scripts]# awkhost5.sh 192.168.1.31  
    oldboy31.etiantian.org  
    [root@old_boy scripts]# awkhost5.sh 192.168.1.11  
    oldboy11.etiantian.org  
    oldboy111.etiantian.org ------>这里有bug了。  
    [root@old_boy scripts]# awkhost5.sh 192.168.1.21  
    oldboy21.etiantian.org  
    改进下来排除bug:  
    [root@old_boy scripts]# cat awkhost5-1.sh   
    awk '{if($1~/'$1' /) print $2}'  /etc/hosts ==>用上面加空格的思路不对。  
    [root@old_boy scripts]# cat awkhost5-1.sh   
    awk '{if($1~/'$1'$/) print $2}'  /etc/hosts #增加一个正则表达式$  
    执行结果:  
    [root@old_boy scripts]# awkhost5-1.sh 192.168.1.21  
    oldboy21.etiantian.org  
    [root@old_boy scripts]# awkhost5-1.sh 192.168.1.11  
    oldboy11.etiantian.org  
    [root@old_boy scripts]# awkhost5-1.sh 192.168.1.31  
    oldboy31.etiantian.org 
    
    ----------------我是每种方法分隔符---------------
    法7)awk -v精确匹配法
     
    
    命令行测试:  
    [root@old_boy scripts]# awk -v p=192.168.1.21 '$1 == p{print $2}' /etc/hosts  
    oldboy21.etiantian.org  
    [root@old_boy scripts]# awk -v p=192.168.1.11 '$1 == p{print $2}' /etc/hosts  
    oldboy11.etiantian.org  
    [root@old_boy scripts]# awk -v p=192.168.1.11 '$1 == p {print $2}' /etc/hosts  
    oldboy11.etiantian.org  
    实际脚本: 
    [root@old_boy scripts]# cat awkhost6.sh   
    #!/bin/bash  
    #p=$1  
    #awk -v p="$p" '$1 == p{print $2}' /etc/hosts  
    awk -v p="$1" '$1 == p{print $2}' /etc/hosts 
    
    执行结果:
    [root@old_boy scripts]# sh  awkhost6.sh  192.168.1.11
    oldboy11.etiantian.org
    [root@old_boy scripts]# sh  awkhost6.sh  192.168.1.21
    oldboy21.etiantian.org
    提示:
    1)传参非awk程序,因此写法p="$1"
    2)man awk
           -v var=val
           --assign var=val
                  Assign the value val to the variable var, before execution of the program begins.   Such  vari-
                  able values are available to the BEGIN block of an AWK program.
    ----------------我是每种方法分隔符---------------
    法8:精确匹配简单的写法
    
    [root@old_boy scripts]# cat awkhost9.sh   
    awk  '$1 == "'$1'" {print $2}' /etc/hosts  
    执行结果:  
    [root@old_boy scripts]# sh awkhost9.sh  192.168.1.11  
    oldboy11.etiantian.org  
    [root@old_boy scripts]# sh awkhost9.sh  192.168.1.21  
    oldboy21.etiantian.org  
    [root@old_boy scripts]# sh awkhost9.sh  192.168.1.31  
    oldboy31.etiantian.org  
    特别提示:这里老男孩老师大量的使用了awk的不同方法来实现同样的功能,很强大吧,  
    希望同学们能按照老师的教学要求精通之。 
    
    ----------------我是每种方法分隔符---------------
    法9:学生的一个不成熟的实现法
    
    #!/bin/bash  
    b=/$PWD/wang.txt  
    echo -n "plase input ip : " 
    read a  
    if [ $a == "192.168.1.11" ]  
            then 
    cat $b | grep $a | awk -F ' ' '{print $2}' 
     
    elif [ $a  == "192.168.1.21" ]   
            then 
    cat $b | grep $a | awk -F ' ' '{print $2}' 
     
    elif [ $a  == "192.168.1.31" ]  
            then 
    cat $b | grep $a | awk -F ' ' '{print $2}' 
            else 
    echo "plase input the correct IP address " && exit 1  
    fi  
    提示:大家看看问题在哪?脚本不通用。  
     
    
    ------老男孩老师改进后 
    
    #!/bin/bash  
    #author oldboy  
    #qq 31333741  
    hosts_file="$PWD/oldboy.txt" 
    #judge file  
    [ ! -f $hosts_file ] && echo "no test file!" && exit 1  
    echo -n "plase input ip : " 
    read ip  
    #judge ip format  
    [ "${#a}" -lt 8 ] && [ "`echo $ip|sed 's/[0-9]//g'`" != "..." ] &&   
    echo "Plase input the correct IP address" && exit 1  
     
    #start  
    result1=$(grep "$ip" $hosts_file|awk '{print $1}')  
    if [ "$ip" == "$result1" ]  
      then   
            grep "$ip" $hosts_file|awk '{print $2}' 
            exit 0  
    else 
            echo  "Not find the hostname of $ip" 
            exit 1  
    fi  
    提示:此法不可取,画蛇添足了。  

    自定义变量的建议

    自定义变量的建议:

    (1)纯数字(不带空格),定义方式可以不加引号(单或双),例如:

    A.oldboy=33

    B.NETWORKING=yes

    (2)没特殊情况,字符串一般用双引号定义,特别是多个字符串中间有空格时,例如:

    A.NFSD_MODULE="no load"
    
    B.MyName="Oldboy is a handsome boy."

    (3)变量内容需要原样输出时,要用单引号('')。

    A.OLDBOY_NAME='OLDBOY'

    变量的命名规范

    1)变量命名要统一,使用全部大写字母,如APACHE_ERR_NUM;语义要清晰,能够正确表达变量内容的含义,过长的英文单词可采用前几个字符代替。多个单词连接使用”_”号连接,引用时,最好以${APACHE_ERR_NUM}加大括号或”${APACHE_ERR_NUM}”外面加双引号方式引用变量:

    2)避免无含义字符或数字:例如下面的COUNT,并不知道其确切含义;

    范例1:COUNT的不确切定义

    COUNT=`grep keywords file`
    
    if [ ${COUNT} -ne 22 ]
    
    then
    
    Echo ‘Do Something’
    
    fi

    3)全局变量和局部变量命名

    A.脚本中的全局变量定义,如OLDBOY_HOME或OLDBOYHOME,在变量使用时,使用{}将变量括起或”${OLDBOY_HOME}”

    范例2:操作系统函数库脚本内容全局变量截取例子

    [root@Web ~]# cat /etc/init.d/functions
    
    # -*-Shell-script-*-
    
    #
    
    # functions     This file contains functions to be used by most or all
    
    #               shell scripts in the /etc/init.d directory.
    
    #
    
    TEXTDOMAIN=initscripts

    B.脚本中局部变量定义:存在于脚本函数(function)中的变量称为局部变量,要以local方式进行声明,使之只在本函数作用域内有效,防止变量在函数中的命名与变量外部程序中变量重名造成持续异常。下面是函数中的变量定义例子:

    范例3:函数内的变量定义

    Function TestFunc()
    
    {
    
    local i
    
    for((i=0;i<n;i++))
    
    do
    
    echo ‘do something’
    
    done
    
    }

    范例4:操作系统函数库脚本内容局部函数变量截取例子

    # Check if any of $pid (could be plural) are running
    
    checkpid() {
    
            local i
    
     
    
            for i in $* ; do
    
                    [ -d "/proc/$i" ] && return 0
    
            done
    
            return 1
    
    }

    4)变量合并:当某些变量或配置项要组合起来才有意义时,如文件的路径和文件名称,建议将要组合的变量合并到一起赋值给一个新的变量,这样既方便之后的调用,也为以后进行修改提供了方便。

    范例5:自动化安装httpd的脚本变量合并定义

    VERSION="2.2.22"
    
    SOFTWARE_NAME="httpd"
    
    SOFTWARE_FILENAME="${SOFTWARE_NAME}-${VERSION}.tar.gz"

    5)变量定义总结:多学习模仿操作系统自带的/etc/init.d/functions函数库脚本的定义思路。

    扩展赋值操作

    三种定界符    注释
    双引号""    允许扩展,通过$引用其他变量
    单引号''    禁用扩展,即便$也视为普通字符
    反撇号``    将命令的执行输出作为变量值  #执行命令本身的功能,$()与``等效,但$()更方便嵌套使用 

    命令替换:

     $(COMMAND), 反引号:`COMMAND`

    例子:把命令中某个子命令替换为其执行结果的过程

    touch ./file-$(date +%F-%H-%M-%S).txt

      rootdata=`df -h | grep /dev/sda2 | awk '{print $4}' |  awk -F"G" '{print $1}'`

     写一个脚本,分别显示当前系统上所有默认shell为bash的用户和默认shell为/sbin/nologin的用户,并统计各类shell下的用户总数。显示结果形如:

    BASH,3users,they are:
    root,redhat,gentoo
    NOLOGIN, 2users, they are:
    bin,ftp
    
     

    实现脚本:

    #!/bin/bash
    #
    NUMBASH=`grep "bash$" /etc/passwd | wc -l`
    BASHUSERS=`grep "bash$" /etc/passwd | cut -d: -f1`
    BASHUSERS=`echo $BASHUSERS | sed 's@[[:space:]]@,@g'`
    
    echo "BASH, $NUMBASH users, they are:"
    echo "$BASHUSERS

     例子:

    [root@Web ~]#ls
    test.sh
    [root@Web ~]#CMD=`ls`  
    [root@Web ~]#echo $CMD
    test.sh
    [root@Web ~]#CMD1=$(pwd)
    [root@Web ~]#echo $CMD1
    /root

    提示:

    1、"CMD=`ls`"注意命令变量前后的字符``
    2、在变量名前加$,可以取得此变量的值,使用echo命令可以显示变量的值,$A和${A}的写法不同,但功能是一样的,推荐使用后者的语法或"${A}"的用法。
    3、${WEEK}day若变量和其他字符组成新的变量就必须给变量加上大括号。
    4、养成将所有字符串变量用双引号括起来使用的习惯,将会减少很多编程时遇到的怪异的错误。具体使用方法如:"$A"或"${A}"的语法

    生产环境常见应用:

    1.对站点按天打包生成不同的文件名

    [root@Web ~]# CMD=$(date +%F)
    [root@Web ~]# echo $CMD
    2015-04-12
    [root@Web ~]# echo $(date +%F).tar.gz
    2015-04-12.tar.gz
    [root@Web ~]# H=$(uname -n)
    [root@Web ~]# echo $H
    Web
    [root@Web ~]# tar zcf $H.tar.gz /etc/services 
    tar: 从成员名中删除开头的“/”   #切换到目录下打包就不会出现这种情况
    [root@Web ~]# ll Web.tar.gz 
    -rw-r--r--. 1 root root 127319 4月  12 09:37 Web.tar.gz

    命令的逻辑分隔

    分隔符    含义
    &&    前边命令执行成功了后边的命令才执行,若前边的命令执行失败后边的命令不执行
    ;    顺序执行命令 前边命令执行是否成功不影响后边命令的执行
    ||    只有当左侧的命令执行失败时,右侧的命令才会执行

    从标准输入赋值

    语法:read从键盘读入变量值完成赋值

    read 选项 变量值

    参数选项

    参数选项    注释
    read [-p "提示信息"] 变量名    
    -p可选    
    -t    可指定超时秒数

    终端显示控制

                注释
    -stty -echo    关闭终端输出(无显示)
    -stty echo    恢复终端输出(显示)

    变量的作用范围

    局部变量

    新定义的变量默认只在当前Shell环境中有效
    无法在子Shell环境中使用

     定义本地变量

    本地变量在用户当前的shell生存期的脚本中使用。例如,本地变量ZGY取值为ett098,这个值只在用户当前shell生存期中有意义。如果在shell中启动另一个进程或退出,本地变量ZGY值将无效。

    1.普通字符串变量定义

    变量名=value
    变量名='value'
    变量名="value"

    shell中变量名的要求:一般是字母,数字,下划线组成。字母开头。

    全局变量

    全局变量在当前Shell及子Shell环境中均有效
    使用export可将局部变量声明为全局变量

    export

                        注释
    export 局部变量名[=变量值]    为局部变量添加全局属性
    export -n 全局变量名      取消指定变量的全局属性

    当变量被定义为全局变量后,变量能在当前的Shell和当前Shell的子Shell里使用。

    站在用户登录的角度来说,SHELL的类型:

    登录式shell:

    正常通过某终端登录
    su - USERNAME
    su -l USERNAME

    非登录式shell:

    su USERNAME
    图形终端下打开命令窗口
    自动执行的shell脚本

    bash的配置文件:

    全局配置

    /etc/profile
    /etc/profile.d/*.sh
    /etc/bashrc

    个人配置

    ~/.bash_profile
    ~/.bashrc

    profile类的文件:

    设定环境变量
    运行命令或脚本

    bashrc类的文件:

    设定本地变量
    定义命令别名

    登录式shell如何读取配置文件?

    /etc/profile --> /etc/profile.d/*.sh --> ~/.bash_profile -->  ~/.bashrc --> /etc/bashrc

    非登录式shell如何配置文件?

    ~/.bashrc --> /etc/basrc --> /etc/profile.d/*.sh

    登录式shell,配置文件及次序:

    /etc/profile --> /etc/profile.d/*.sh --> ~/.bash_profile -->  ~/.bashrc --> /etc/bashrc

    非登录式shell

    ~/.bashrc --> /etc/bashrc --> /etc/profile.d/*.sh

    系统级配置文件对所有用户生效

    /etc/profile
    /etc/bashrc

    用户级配置文件只针对用户生效(只针对某个用户生效。在这样的文件里配置可以执行脚本、定义变量、执行命令)

    ~/.bash_profile
    ~/.bashrc

    图形模式  每打开一个新的终端时会调用哪些文件

    /etc/bashrc 
    ~/.bashrc
  • 相关阅读:
    git报错
    rabbitmq关于guest用户登录失败解决方法
    【转】Linux下RabbitMQ服务器搭建(单实例)
    saltstack安装配置(yum)
    linux下搭建禅道项目管理系统
    git用户限制ssh登录服务器
    中央定调,“新基建”彻底火了!这七大科技领域要爆发
    数据可视化使用小贴士,这样的错误别再犯了
    5G国战:一部国家奋斗的血泪史,看看各国是如何角力百年?
    还没有一个人能够把并发编程讲解的这么透彻
  • 原文地址:https://www.cnblogs.com/zhongguiyao/p/8933883.html
Copyright © 2020-2023  润新知