Shell脚本学习指南(二)——变量、条件、循环

变量

  • 基本概念

    • Shell变量的名称与C语言一样,由数字、字母、下划线组成,其中只能以字母或下划线开头
    • 变量可以为空值,null
    • 赋值时,=两边没有空格
    str="abc"
    a=1
    • 取变量的值时,在变量前面加$
    a=1
    echo $a => 1
    • 变量的值如果含空格,赋值时用双引号括起来
    str="Hello World!"
    • 算数展开 $( (…) )
    i=5 j=6
    echo $((i+j))             => 11
    echo $(((i*j)-(i+j)))     => 19
    echo $((3>1))             => 1
    echo $((1>3))             => 0
  • 相关命令

    • export:将shell变量输出为环境变量,或者将shell函数输出为环境变量

      • 打印当前环境变量
      export      => 打印当前进程的所有环境变量
      export -p   =>export
      • 修改当前进程的环境变量
      export DAVID="david"           => 设置环境变量DAVID,值为david
      export PATH=$PATH:/home/david  => 在PATH后面追加/home/david路径
    • readonly:定义只读shell变量和shell函数

      • 打印当前只读变量
      readonly      => 打印当前进程的所有只读变量
      readonly -p   => 同readonly
      • 设置当前进程的只读变量
      readonly DAVID="david"           => 设置只读变量DAVID,值为david
    • env:为将要运行的程序设置环境变量

      • -i

        env -i A=1 B=2 awk 'xxx' file   # 用新的环境变量重新初始化环境变量,然后运行命令
                                        # 新环境变量只对将要运行的命令生效
      • -u

        env -u xxx awk 'xxx' file       # 删除某个环境变量,然后用新的环境变量运行命令
                                        # 新环境变量只对将要运行的命令生效
    • set:更改shell特性

      • -x 显示执行的语句
      • +x 不显示执行的语句
    • unset:删除已经定义的变量或函数
      • -f 删除函数
      • -v 删除变量
  • 参数展开

    • {}展开
    str="Hello World!"
    echo __$str__    => __
    echo __${str}__  => __Hello World!__
    • 未定义的变量会展开为null字符串
    
    # 非常危险!
    
    rm -fr /$UnDeclareVar   => rm -fr /
    • 替换运算符

      • ${varname:-word}
        实现:如果varname存在且非null,则返回其值;否则返回word
        用途:如果变量未定义,则返回一个默认值

        str1="Hello Shell!"
        echo ${str1:-Default} => Hello Shell!
        echo ${str2:-Default} => Default
      • ${varname:=word}
        实现:如果varname存在且非null,则返回其值;否则设置varname=word,并返回varname
        用途:如果变量未定义,则将变量设为默认值并返回该变量

        str1="Hello Shell!"
        echo ${str1:=Default} => Hello Shell!
        echo ${str2:=Default} => Default
        echo $str2            => Default
      • ${varname:?message}
        实现:

        如果varname存在且非null,则返回其值;否则显示varname:message,并退出脚本
        如果省略message,显示默认信息"parameter null or not set"
        注:交互式Shell下不需要退出,不同Shell有不同行为
        

        用途:捕捉由于变量未定义而导致的错误

        str1="Hello Shell!"
        echo ${str1:?str1 is not set} => Hello Shell!
        echo ${str2:?str2 is not set} => line N: str2: str2 is not set
        echo ${str2:?}                => line N: str2: parameter null or not set
      • ${varname:+word}
        实现:如果varname存在且非null,则返回word;否则返回null
        用途:测试变量是否存在

        str1="Hello Shell!"
        echo ${str1:+1} => 1
        echo ${str2:+1} => (null)
      • 以上四种替换运算符,” : “都是可选的。如果省略” : “,则只判断是否存在,不判断是否非空

        替换运算符 用途 实现
        ${varname:-word} 如果变量未定义,则返回一个默认值 如果varname存在且非null,则返回其值;否则返回word
        ${varname:=word} 如果变量未定义,则将变量设为默认值并返回该变量 如果varname存在且非null,则返回其值;否则设置varname=word,并返回varname
        ${varname:?msg} 捕捉由于变量未定义而导致的错误 如果varname存在且非null,则返回其值;否则显示varname:message,并退出脚本;如果省略message,显示默认信息”parameter null or not set”
        ${varname:+word} 测试变量是否存在 如果varname存在且非null,则返回word;否则返回null
        ${varname:-word} 如果变量未定义,则返回一个默认值 如果varname存在,则返回其值;否则返回word
        ${varname:=word} 如果变量未定义,则将变量设为默认值并返回该变量 如果varname存在,则返回其值;否则设置varname=word,并返回varname
        ${varname:?msg} 捕捉由于变量未定义而导致的错误 如果varname存在,则返回其值;否则显示varname:message,并退出脚本;如果省略message,显示默认信息”parameter null or not set”
        ${varname:+word} 测试变量是否存在 如果varname存在,则返回word;否则返回null
    • 模式匹配运算符

      • ${varname#pattern}
        如果模式匹配与变量的开头处,则删除匹配的最短部分,并返回剩下的部分
      • ${varname##pattern}
        如果模式匹配与变量的开头处,则删除匹配的最长部分,并返回剩下的部分
      • ${varname%pattern}
        如果模式匹配与变量的结尾处,则删除匹配的最短部分,并返回剩下的部分
      • ${varname%%pattern}
        如果模式匹配与变量的结尾处,则删除匹配的最长部分,并返回剩下的部分
      str1="/home/davidwang/test/home/test.tar.bz"
      echo ${str1#/*/}   => davidwang/test/home/test.tar.bz
      echo ${str1##/*/}  => test.tar.bz
      echo ${str1%.*}    => /home/davidwang/test/home/test.tar
      echo ${str1%%.*}   => /home/davidwang/test/home/test
    • 字符串长度运算符

      • ${#var}
      str="abcdefghijklmnopqrstuvwxyz"
      echo "$str size is ${#str}"  => abcdefghijklmnopqrstuvwxyz size is 26
  • 位置参数

    • N1N>9 {10}
    • $#:命令行参数的个数
    • , @:一次表示所有参数
    • “$*”:将所有参数当做一个字符串
    • “$@”:将所有参数视为单独的个体
      echo "\$1 = ${1:-/dev/null}"
      ./test.sh       => $1 = /dev/null
      ./test.sh 1 2 3 => $1 = 1
      
      echo "arg num = $#"
      echo "\$* = $*"
      echo "\$@ = $@"
      printf "\"\$*\" = %s\n" "$*"
      printf "\"\$@\" = %s\n" "$@"
      
      ./test.sh 1 2 3 ==>
      arg num = 3
      $* = 1 2 3
      $@ = 1 2 3
      "$*" = 1 2 3
      "$@" = 1
      "$@" = 2
      "$@" = 3
  • 特殊变量

变量 意义
0 Shell程序名称
# 参数个数
@ 命令行参数,如果在”“内,则展开为一系列单独的参数
* 命令行参数,如果在”“内,则把所有参数合并为一个字符串
$ 当前进程ID
PPID 父进程ID
? 前一个命令的退出状态
! 最近一个后台命令的进程编号
HOME 用户根目录
PATH 命令的查找路径
PWD 当前工作目录
LINENO 行号
- 传给Shell的选项
PS1 主要的命令提示字符串;默认为$
PS2 行继续的提示字符串;默认为>
PS4 以set -x设置的执行跟踪的提示字符串;默认为+
LANG 当前local默认名称;其他LC_*变量会覆盖其值
LC_ALL 当前local名称;会覆盖LANG和其他LC_*变量
LC_COLLATE 用来排序字符的当前local名称
LC_CTYPE 在模式匹配期间,用来确定字符类别的当前local名称
LC_MESSAGES 输出信息的当前语言的名称
ENV 仅用于交互式Shell;要读取和在启动时要执行的一个文件的完整路径名;XSI必须的变量
IFS 内部的字段分隔器
NLSPATH 在$LC_MESSAGES(XSI)所给定的信息语言里,信息目录的位置

条件

  • 退出状态
    • 每一条命令,不管是内置的还是外部的,退出时均返回一个整数值
    • 0表示成功,非0表示失败,用$?访问
    • exit命令:传递一个退出值给调用者;如果没有提供退出值,默认为最后一个命令的退出状态
  • if语句

    if [[ condition ]]; then
        #statements
    elif [[ condition ]]; then
        #statements
    else
        #statements
    fi
  • test命令

    • 两种形式

      str1="abc"
      str2="abc"
      
      if test $str1 = $str2; then
          echo "str1 == str2"
      else
          echo "str1 != str2"
      fi
      
      
      # 下面的形式等同于上面的形式
      
      if [[ $str1 = $str2 ]]; then
          echo "str1 == str2"
      else
          echo "str1 != str2"
      fi
    • 运算符

    运算符 如果…则为真
    -e file file存在
    -s file file不为空
    -f file file是一般文件
    -d file file是目录
    -b file file是块设备文件
    -c file file是字符设备文件
    -p file file是命名管道(FIFO)
    -S file file是socket
    -h file file是符号链接
    -L file file是符号链接,同-h
    -r file file是可读的
    -w file file是可写入的
    -x file file是可执行的,或file是可被查找的目录
    -g file file有设置setgid位
    -u file file有设置setuid位
    string string不是null
    -n string string非null
    -z string string是null
    s1 = s2 字符串s1等于s2
    s1 != s2 字符串s1不等于s2
    n1 -eq n2 整数n1等于n2
    n1 -ne n2 整数n1不等于n2
    n1 -lt n2 整数n1小于n2
    n1 -gt n2 整数n1大于于n2
    n1 -le n2 整数n1小于等于n2
    n1 -ge n2 整数n1大于等于n2
    -t n 文件描述符n指向以终端
    
    # 在test中,所有变量展开都需要用引号括起来
    
    if[ -f "$file" ]  ==> 正确
    if[ -f $file ]    ==> 如果$file为空,shell的行为无法预料
  • case语句

    case word in
        pattern )
            ;;      # 执行到;;结束
        * )         # 默认动作,相当于C语言中的default
            ;;
    esac

循环

  • for语句

    for i in words; do
        #statements
    done
    
    for i; do   #相当于for i in "$@"
        #statements
    done
    
    for (( i = 0; i < 10; i++ )); do
        #statements
    done
  • while与until语句

    while [[ condition ]]; do # condition为真时执行
        #statements
    done
    
    until [[ condition ]]; do # condition为假时执行
        #statements
    done
  • break与continue

    • break:跳出循环
    • continue 继续执行下一个循环
    • break与continue,都可以接受数值参数,控制语句跳出或继续执行多少个循环
    while [[ condition ]]; do
        while [[ condition ]]; do
            #statements
            break 2  # 跳出最外面的循环
        done
    done
  • shift

    • 依次处理命令行参数
    • 每次执行后,原来的 1 2的旧值取代, 2 3的旧值取代…,$#也会依次减1
    • 有一个可选参数,指定每一次移动几位,默认为1
    file= verbose= quiet= long=
    while [[ $# -gt 0 ]]; do                 # 依次处理命令行参数,没处理依次,$#都减1 
        case $1 in
            -f )
                file=$2
                echo "file = $file"
                shift                        # 因为-f选项后面要跟文件名,所以还得再shift一遍
                ;;
            -v )
                verbose=true
                quiet=false
                echo "verbose = $verbose"
                echo "quiet = $quiet"
                ;;
            -q )
                quiet=true
                verbose=false
                echo "verbose = $verbose"
                echo "quiet = $quiet"
                ;;
            -l )
                long=true
                echo "long = $long"
                ;;
        esac
        shift
    done
  • getopts

    • 简化命令行参数处理
    • 第一个参数是所有合法参数的字符串;如果选项字母后面跟冒号,说明该选项后面需要一个参数,且是必填的;如果遇到需要参数的选项,将参数值放置到变量OPTARG中
    • OPTIND包含下一个要处理的参数的索引值,初始值为1
    • 第二个参数为变量名称,每次getopts时,该变量会被更新,值等于找到的选项字母;遇到不合法的选项时,该变量被置为?
    file= verbose= quiet= long= a=
    
    while getopts f:vqa:l opt
    do
        case $opt in
            f )
                file=$OPTARG
                echo "file = $file"
                ;;
            a )
                a=$OPTARG
                echo "a = $a"
                ;;
            v )
                verbose=true
                quiet=false
                echo "verbose = $verbose"
                echo "quiet = $quiet"
                ;;
            q )
                verbose=false
                quiet=true
                echo "verbose = $verbose"
                echo "quiet = $quiet"
                ;;
            l )
                long=true
                echo "long = $long"
                ;;
        esac
    done

函数

  • 函数使用前必须先定义
  • 在脚本的起始处定义
  • 在一个独立文件里定义,在脚本中用”.”符号获取

    wait_for_user(){
        XXX
    }
  • return

    • 从函数里返回值
    • 如果为指定参数,使用默认退出状态
      return $?  # 严谨的写法
  • 函数参数

    • 在函数中,位置参数代表函数参数;当函数完成时,原来的命令行参数会恢复

涉及到的命令

  • export
  • readonly
  • env
  • unset
  • exit
  • return
  • test
  • shift
  • getopts

你可能感兴趣的:(Shell脚本)