zoukankan      html  css  js  c++  java
  • 如来神掌第二式第一招----Shell脚本基础

    ###############################################################################
    # Name : Mahavairocana                                                                                                                                           
    # Author : Mahavairocana                                                                                                                                         
    # QQ : 10353512                                                                                                                                                    
    # WeChat : shenlan-qianlan                                                                                                                                      
    # Blog : http://www.cnblogs.com/Mahavairocana/                                                                                                       
    # Description : You are welcome to reprint, or hyperlinks to indicate the                                                                        
    #                    source of the article, as well as author information.                                                                                ###############################################################################

    一、术语解释

    1、变量:

    变量类型:
    本地变量:    只对当前shell进程有效的,对当前进程的子进程和其它shell进程无效。
                定义:VAR_NAME=VALUE
                变量引用:${VAR_NAME}
                取消变量:unset VAR_NAME
                相当于java中的私有变量(private),只能当前类使用,子类和其他类都无法使用。

                环境变量:    自定义的环境变量对当前shell进程及其子shell进程有效,对其它的shell进程无效
                定义:export VAR_NAME=VALUE
                对所有shell进程都有效需要配置到配置文件中
                vi /etc/profile
                source /etc/profile
                相当于java中的protected修饰符,对当前类,子孙类,以及同一个包下面可以共用。
                            
    局部变量:  在函数中调用,函数执行结束,变量就会消失
                对shell脚本中某代码片段有效
                定义:local VAR_NAME=VALUE
                相当于java代码中某一个方法中定义的变量,只对这个方法有效。

                变量位置:    $1,$2,.....${10}....
                test.sh 3 89
                $0:脚本自身
                $1:脚本的第一个参数
                $2:脚本的第二个参数
                相当于java中main函数中的args参数,可以获取外部参数。

                特俗变量:    $?:接收上一条命令的返回状态码
                返回状态在0-255之间
                $#:参数个数
                $*:或者$@:所有的参数
                $$:获取当前shell的进程号(PID)(可以实现脚本自杀)(或者使用exit命令直接退出也可以使用exit [num])



    变量定义规范
    命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
    中间不能有空格,可以使用下划线(_)。
    不能使用标点符号。
    不能使用bash里的关键字(可用help命令查看保留关键字)。

    2、函数:方便程序和管理和模块化并减少代码的重复

    3、引号区别:

        双引号(" "):在双引号中,除了$, '', `和以外所有的字符都解释成字符本身。
    root@gyb-ubuntu:~# echo "$PATH"  
    /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games  
        单引号(' '):在单引号中所有的字符包括特殊字符($,'',`和)都将解释成字符本身而成为普通字符。
    root@gyb-ubuntu:~# echo '$PATH'  
     $PATH  
        反引号(` `):在反引号中的字符串将解释成shell命令来执行。
    root@gyb-ubuntu:~# echo `ls`  
    99.sh cloud_curr_design cloud_curr_design.tar.gz exefile for.sh gyb_virsh httpd-2.2.31 qemu_help readfile.sh switch.sh temp temp10.sh temp1.sh temp2.sh temp3.sh temp4.sh temp5.sh temp6.sh temp7.sh temp8.sh temp9.sh te.sh test9.sh ubuntu1204Server.img ubuntu1204Server.xml ubuntuGuest.xml ubuntu-server.img win7.img 

    文件测试:

    文件状态测试
    -b filename : 当filename 存在并且是块文件时返回真(返回0)
    -c filename : 当filename 存在并且是字符文件时返回真
    -d pathname : 当pathname 存在并且是一个目录时返回真
    -e pathname : 当由pathname 指定的文件或目录存在时返回真
    -f filename : 当filename 存在并且是正规文件时返回真
    -g pathname : 当由pathname 指定的文件或目录存在并且设置了SGID 位时返回真
    -h filename : 当filename 存在并且是符号链接文件时返回真 (或 -L filename)
    -k pathname : 当由pathname 指定的文件或目录存在并且设置了"粘滞"位时返回真
    -p filename : 当filename 存在并且是命名管道时返回真
    -r pathname : 当由pathname 指定的文件或目录存在并且可读时返回真
    -s filename : 当filename 存在并且文件大小大于0 时返回真
    -S filename : 当filename 存在并且是socket 时返回真
    -t fd       : 当fd 是与终端设备相关联的文件描述符时返回真
    -u pathname : 当由pathname 指定的文件或目录存在并且设置了SUID 位时返回真
    -w pathname : 当由pathname 指定的文件或目录存在并且可写时返回真
    -x pathname : 当由pathname 指定的文件或目录存在并且可执行时返回真
    -O pathname : 当由pathname 存在并且被当前进程的有效用户id 的用户拥有时返回真(字母O 大写)
    -G pathname : 当由pathname 存在并且属于当前进程的有效用户id 的用户的用户组时返回真
    file1 -nt file2 : file1 比file2 新时返回真
    file1 -ot file2 : file1 比file2 旧时返回真
    举例: if [ -b /dev/hda ] ;then echo "yes" ;else echo "no";fi // 将打印 yes
    test -c /dev/hda ; echo $? // 将打印 1 表示test 命令的返回值为1,/dev/hda 不是字符设备
    [ -w /etc/passwd ]; echo $? // 查看对当前用户而言,passwd 文件是否可写
     
    测试时逻辑操作符
    -a 逻辑与,操作符两边均为真,结果为真,否则为假。
    -o 逻辑或,操作符两边一边为真,结果为真,否则为假。
    !  逻辑否,条件为假,结果为真。
    举例: [ -w result.txt -a -w score.txt ] ;echo $? // 测试两个文件是否均可写
     
    常见字符串测试
    -z string    : 字符串string 为空串(长度为0)时返回真
    -n string    : 字符串string 为非空串时返回真
    str1 = str2  : 字符串str1 和字符串str2 相等时返回真
    str1 != str2 : 字符串str1 和字符串str2 不相等时返回真
    str1 < str2  : 按字典顺序排序,字符串str1 在字符串str2 之前
    str1 > str2  : 按字典顺序排序,字符串str1 在字符串str2 之后
    举例: name="zqf"; [ $name = "zqf" ];echo $? // 打印 0 表示变量name 的值和字符串"zqf"相等
     
    常见数值测试
    int1 -eq int2 : 如果int1 等于int2,则返回真
    int1 -ne int2 : 如果int1 不等于int2,则返回真
    int1 -lt int2 : 如果int1 小于int2,则返回真
    int1 -le int2 : 如果int1 小于等于int2,则返回真
    int1 -gt int2 : 如果int1 大于int2,则返回真
    int1 -ge int2 : 如果int1 大于等于int2,则返回真
    举例: x=1 ; [ $x -eq 1 ] ; echo $? // 将打印 0 表示变量x 的值等于数字1
    x=a ; [ $x -eq "1" ] // shell 打印错误信息 [: a: integer expression expected

    二、基础语法

    1、for

    for i in {1..10}
    do
        echo $i
    done

    2、if

    if [ "$1" = 1 ];then
       echo "number is 1"
    elif [ "$1" = 2 ];then
       echo "number is 2"
    elif [ "$1" = 3 ];then
       echo "number is 3"
    else
    echo "number not is 1 2 3"
    fi

    注意:$1 不加引号的时候会报错“line 4: [: =: unary operator expected” 报错的原因是:如果变量$1的值为空,那么就if语句就变成了if [  ="yes" ],这不是一个合法的条件。为了避免出现这种情况,我们必须给变量加上引号if [ "$1"="yes" ],这样即使是空变量也提供了合法的测试条件,,if [  " "="yes"  ]

    3、while 也称为前测试循环语句,重复次数是利用一个条件来控制是否继续重复执行这个语句。为了避免死循环,必须保证循环体中包含循环出口条件即表达式存在退出状态为非0的情况。

    #!/bin/bash  
    sum=0  
    i=1  
    while(( i <= 10 ))  
    do  
         let "sum+=i"  
         let "i += 2"     
    done  
      
    echo "sum=$sum"
    
    #!/bin/bash
    
    cat 1 | while read line
    do 
    echo $line
    done

    4、until: until命令和while命令类似,while能实现的脚本until同样也可以实现,但区别是until循环的退出状态是不为0,退出状态是为0(与while刚好相反),即whie循环在条件为真时继续执行循环而until则在条件为假时执行循环。

    #!/bin/bash  
      
    i=0  
      
    until [[ "$i" -gt 5 ]]    
    do  
        let "num=i*i"  
        echo "$i * $i = $num"  
        let "i++"  
    done 

    5、select :select结构从技术角度看不能算是循环结构,只是相似而已,它是bash的扩展结构用于交互式菜单显示,功能类似于case结构比case的交互性要好。

    #!/bin/bash  
    
    echo "What is your favourite num? "  
    
    select num in "1" "2" "3" 
    do
        break
    done
    
    echo "You have selected $num" 

    6、case

    case $1 in 
        1 | 2) # arg in pattern or sample 
        echo "number is 1 or 2" ;; 
        3) # arg in pattern1 
        echo "number is 3" ;; 
        *) #default 
        echo "number not is 1 2 3";; 
    esac  

    7、case+select

    #!/bin/sh 
    
    select ch in "start" "stop" "restart" "exit"
    do
    case $ch in
    "start")
        echo "start" 
        ;;
    "stop")
        echo "stop" 
        ;;
    "restart")
        echo "restart" 
        ;;
    "exit")
        echo "exit" 
        break;
        ;;
    *)
        echo "Ignorant" 
        ;;
    esac
    done; 

    8、函数

    #! /bin/bash  
    
    function list ()  
    {
    echo "Mahavairocana "
    }
    
    function list1 ()  
    {
    echo "Mahavairocana1 "
    }
    
    list
    list1

    9、break: break命令是在处理过程中跳出循环的一种简单方法,可以使用break命令退出任何类型的循环,包括while循环和until循环
      1:跳出单循环
      2:跳出内循环
      使用多循环时break命令自动终止你所在的最里面的循环,注意,当内循环被break命令终止,外循环会继续执行。
      3:跳出外循环
      可能有时处于内循环但需要停止外循环,break命令后面就需要指定一个参数了
      break n
      n表明要跳出的循环级别,默认情况下,n是1,代表跳出当前循环,如果将n设置为2,break命令将停止外循环的下一级循环。

    #!/bin/bash
    while [ 1 -eq 1 ]
    do
      for ((i=0;i<10;i++))
      do
        if [ $i -eq 2 ]
            then
            break ;
        fi
      echo $i
      done
    echo 'yes'
    sleep
    done

    10 、continue:continue命令是一种提前停止循环内命令,而不完全终止循环的方法,这就需要在循环内设置shell不执行命令的条件

    #!/bin/bash
    for ((i=0;i<10;i++))
    do
    if [ $i -eq 2 ]
    then
    continue
    fi
    echo $i
    done

    11、shift:位置参数可以用shift命令左移,shift n表示把第n+1个参数移到第1个参数, 即命令结束后$1的值等于$n+1的值, 而命令执行前的前面n个参数不能被再次引用, 后面$#-n+1到$#的参数被unset, 参数的个数减少为$#-n个.n的值不能为负数, 若n为0或大于参数个数$#则参数不变, 若n没有给定则默认为1. 当n小于0或者大于参数个数$#时shift命令的返回值大于0, 否则返回0.

    #!/bin/bash   
      
    while [ "$#" -gt 0 ]  
    do   
        echo $*  
        shift  
    done  
      
    运行结果:  
    [root@nn shell]# ./shift_fun1.sh 9 8 7 6 5 4 3 2 1  
    9 8 7 6 5 4 3 2 1  
    8 7 6 5 4 3 2 1  
    7 6 5 4 3 2 1  
    6 5 4 3 2 1  
    5 4 3 2 1  
    4 3 2 1  
    3 2 1  
    2 1  
    1 

    12、getopts 的设计目标是在循环中运行,每次执行循环,getopts 就检查下一个命令行参数,并判断它是否合法。即检查参数是否以 - 开头,后面跟一个包含在 options 中的字母。如果是,就把匹配的选项字母存在指定的变量 variable 中,并返回退出状态0;如果 - 后面的字母没有包含在 options 中,就在 variable 中存入一个 ?,并返回退出状态0;如果命令行中已经没有参数,或者下一个参数不以 - 开头,就返回不为0的退出状态。

    #!/bin/bash  
    
    while getopts "a:b:cdef" opt; do  #“a:b:cdef”第一个冒号代表的含义是:第一个冒号表示忽略错误,即当出现没有的选项是会忽略;字符后面的冒号
    表示该选项必须有自己的参数, 
      case $opt in
        a)
          echo "this is -a the arg is ! $OPTARG"   
          ;;
        b)
          echo "this is -b the arg is ! $OPTARG"   
          ;;
        c)
          echo "this is -c the arg is ! $OPTARG"   
          ;;
        ?)
          echo "Invalid option: -$OPTARG"   
          ;;
      esac
    done

    13、sleep指定延迟时间

    sleep : 默认以秒为单位。
    usleep : 默认以微秒为单位。
    1s = 1000ms = 1000000us
    sleep 不但可以用秒为单位,还可以指定延迟的单位,例如:
    sleep 1s 表示延迟一秒
    sleep 1m 表示延迟一分钟
    sleep 1h 表示延迟一小时
    sleep 1d 表示延迟一天



    三、小技巧

      1、注释

    1.多行注释:
      1. 首先按esc进入命令行模式下,按下Ctrl + v,进入列(也叫区块)模式;
      2. 在行首使用上下键选择需要注释的多行;
      3. 按下键盘(大写)“I”键,进入插入模式;
      4. 然后输入注释符(“//”、“#”等);
      5. 最后按下“Esc”键。 注:在按下esc键后,会稍等一会才会出现注释,不要着急~~时间很短的
     
    2.删除多行注释:
      1. 首先按esc进入命令行模式下,按下Ctrl + v, 进入列模式;
      2. 选定要取消注释的多行;
      3. 按下“x”或者“d”. 注意:如果是“//”注释,那需要执行两次该操作,如果是“#”注释,一次即可
    
    3.多行删除
    1.首先在命令模式下,输入“:set nu”显示行号; 2.通过行号确定你要删除的行; 3.命令输入“:32,65d”,回车键,32-65行就被删除了,很快捷吧
    如果无意中删除错了,可以使用‘u’键恢复(命令模式下)

      2、定义自己的专用vim  

      3、将脚本放置到后台运行

    在脚本后面加一个&
    test.sh &
    这样的话虽然可以在后台运行,但是当前会话窗口关闭之后这个脚本也会停止运行
    nohup命令
    不挂断的运行命令,忽略所有挂断(SIGHUP)信号
    使用nohup test.sh &
    nohup命令将进程和终端分开,所以关闭当前会话窗口不会影响这个进程的执行。
    nohup会在当前执行的目录生成一个nohup.out日志文件

      4、let命令用法

    #!/bin/bash
    let a=5*4 b=9/3
    echo $a $b

      5、shell脚本并发

    实例1:
     #!/bin/bash
    start=`date +%s` #定义脚本运行的开始时间
     
    for ((i=1;i<=10;i++))
    do
    {
            sleep 1  #sleep 1用来模仿执行一条命令需要花费的时间(可以用真实命令来代替)
            echo 'success'$i; 
     }&              #用{}把循环体括起来,后加一个&符号,代表每次循环都把命令放入后台运行
                     #一旦放入后台,就意味着{}里面的命令交给操作系统的一个线程处理了
                     #循环了1000次,就有1000个&把任务放入后台,操作系统会并发1000个线程来处理
                     #这些任务         
    done    
    wait             #wait命令的意思是,等待(wait命令)上面的命令(放入后台的)都执行完毕了再
                     #往下执行。
                     #在这里写wait是因为,一条命令一旦被放入后台后,这条任务就交给了操作系统
                     #shell脚本会继续往下运行(也就是说:shell脚本里面一旦碰到&符号就只管把它
                     #前面的命令放入后台就算完成任务了,具体执行交给操作系统去做,脚本会继续
                     #往下执行),所以要在这个位置加上wait命令,等待操作系统执行完所有后台命令
    end=`date +%s`  #定义脚本运行的结束时间
     
    echo "TIME:`expr $end - $start`"
    
    实例2:
    #!/bin/bash
    start_time=`date +%s`              #定义脚本运行的开始时间
    [ -e /tmp/fd1 ] || mkfifo /tmp/fd1 #创建有名管道
    exec 3<>/tmp/fd1                   #创建文件描述符,以可读(<)可写(>)的方式关联管道文件,这时候文件描述符3就有了有名管道文件的所有特性
    rm -rf /tmp/fd1                    #关联后的文件描述符拥有管道文件的所有特性,所以这时候管道文件可以删除,我们留下文件描述符来用就可以了
    for ((i=1;i<=1;i++))    #i<=1 控制并发数量
    do
            echo >&3                   #&3代表引用文件描述符3,这条命令代表往管道里面放入了一个"令牌"
    done
     
    for ((i=1;i<=100;i++))
    do
    read -u3                           #代表从管道中读取一个令牌
    {
            sleep 2  #sleep 1用来模仿执行一条命令需要花费的时间(可以用真实命令来代替)
            echo 'success'$i       
            echo >&3                   #代表我这一次命令执行到最后,把令牌放回管道
    }&
    done
    wait
     
    stop_time=`date +%s`  #定义脚本运行的结束时间
    echo "TIME:`expr $stop_time - $start_time`"
    exec 3<&-                       #关闭文件描述符的读
    exec 3>&-                       #关闭文件描述符的写
    
    实例3:
    #!/bin/bash
    count=0
    Times=`date +%F-%H-%M`
    rm -rf ./log/*
    PASSWDPath=./log/passwd.txt
    >./log/run.log
    
    if [ ! -d "./log/host" ]; then
            mkdir "./log/host"
    fi
    
    for i in `cat config/hosts.txt | grep -v "^#"` ;do
            if [[ "$count" -ge 20 ]] ;then    #控制通知并发执行20次
            count=0
                    sleep 10
            fi
                    let count+=1
          {
            export server=`echo $i | awk -F "|" '{print $1}'`
            export port=`echo $i | awk -F "|" '{print $2}'`
            export user=`echo $i | awk -F "|" '{print $3}'`
            export passwd=`echo $i | awk -F "|" '{print $4}'`
            export rootpasswd=`echo $i | awk -F "|" '{print $5}'`
            export su=`echo $i | awk -F "|" '{print $6}'`
    
            export cmdfile="config/commands.txt"
            nc -v -w 4 -z $server $port
            if [ $? -eq 0 ]
            then
            ./expect-run.bmc $server $port $user $passwd $rootpasswd $cmdfile
                    if [ $? -ne 0  ] ;then
                    echo "$server-----error" >>$PASSWDPath
                    else
                    echo "$server-----ok">>$PASSWDPath
                    fi
            else
                    echo "$server-----down">>$PASSWDPath
                    continue
    
            fi
            }&
    done
    wait
  • 相关阅读:
    mvc5+ef6+Bootstrap 项目心得--创立之初
    C# StopWatch的使用
    MVC Form异步请求
    bootstrap-datepicker带中文的js文件
    bootstrap
    mvc5+ef6+Bootstrap 项目心得--WebGrid
    mvc5+ef6+Bootstrap 项目心得--身份验证和权限管理
    关于mvc5+EF里面的db.Entry(model).State = EntityState.Modified报错问题
    将DBF文件导入Sqlserver数据库
    MySQL主从报错解决:Failed to initialize the master info structure
  • 原文地址:https://www.cnblogs.com/Mahavairocana/p/8213995.html
Copyright © 2011-2022 走看看