• SHELL编程


    SHELL编程

    一、Shell语法
    1.1、变量声明

    • 1.2、数组声明和使用1.3、特殊的变量
      1.4、运算和运算符
    • 1.4.1、整数运算符
    • 1.5、流程语句
      1.5.1、if 语句
    • 1.5.2、case语句
    • 1.5.3、select 语句
      1.6、循环语句
      1.6.1、for语句

      1.6.2、while 语句
      1.6.3、breake 和continue
      1.6.4、shift三、shell程序调试
      四、信号
      五、bash中常用的命令

    SHELL编程


    一、Shell语法
    1.1、变量声明

    变量=值 (注意:等号两侧不能有空格) 
    a=”hello”
    b=9
    unset a 撤销变量 a
    readonly a=2 声明静态的变量 a=2 ,不能 unset
    export 变量名 可把变量提升为全局环境变量,可供其他shell程序使用

    变量应用中要注意:
    echo "$1 = $1 ; $2 = $2 "; 屏蔽$1 ,屏蔽 $2 ,直接显示 $1 ,$2,同理*也屏蔽了 * 的通配符作用
    ${SAT}day ,变量要与字符串连在一起时,应该用 ${} 
    a= `ls -al` 反引号,运行里面的命令,并把结果返回给变量a
    a=$(ls -al) 等价于反引号
    单引号‘’和双引号“”的区别,单引号完全屏蔽 $a 变量,双引号不屏蔽$a,单引号和双引号都屏蔽 * 的通配符作用。


    1.2、数组声明和使用name[0]="Tom"
    name[1]="Tomy"
    name[2]="John"

    name=("Tom" "Tomy" "John")

    例子:
    #!/bin/bash
    name=("Tom" "Tomy" "John")
    for i in 0 1 2
    do
    echo $i:${name[$i]}

    1.3、特殊的变量
    $0:脚本名字。此变量包含地址,可以使用basename $0获得脚本名称。
    $1:第一个参数
    $2,$3,$4,$5,...一次类推。
    $# 传递到脚本的参数个数
    $* 以一个单字符串显示所有向脚本传递的参数,,以("$1 $2...") 
    $$ 脚本运行的ID号
    $! 后台运行的最后一个进程的ID号
    $@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。("$1""$2"...) 
    $- 显示shell使用的当前选项。
    $? 显示最后命令的推出状况。0表示没有错误。

    例子:
    #!/bin/sh
    if [ $# -ne 2 ] ; then                     #if与[ 之间有空格,[与表达式之间有空格,表达式与]之间有空格
    echo "Usage: $0 string file";
    exit 1;
    fi
    grep $1 $2 ;
    if [ $? -ne 0 ] ; then
    echo "Not Found "$1" in $2";
    exit 1;
    fi
    echo "Found "$1" in $2";

    上面的例子中使用了$0 $1 $2 $# $? 等变量,下面是程序的解释: 
    判断运行参数个数,如果不等于2,显示使用"用法帮助", 其中 $0 表示就是脚本自己。 
    用grep 在$2 文件中查找$1 字符串。 
    判断前一个命令运行后的返回值(一般成功都会返回0, 失败都会返回非0)。 
    如果没有成功显示没找到相关信息,否则显示找到了。 
    其中"表示转义,在"" 里面还需要显示"号,则需要加上转义符"

    参数置换的变量
    1、变量=${参数:-word}:如果设置了参数,则用参数的值置换变量的值,否则用word置换。即这种变量的值等于某一个参数的值,如果该参数没有设置,则变量就等于word的值。
    [ -z "${COLUMNS:-}" ] && COLUMNS=80
    2、变量=${参数:=word}:如果设置了参数,则用参数的值置换变量的值,否则把变量设置成word,然后再用word替换参数的值。注意,位置参数不能用于这种方式,因为在Shell程序中不能为位置参数赋值。
    3、变量=${参数:?word}:如果设置了参数,则用参数的值置换变量的值,否则就显示word并从Shell中退出,如果省略了word,则显示标准信息。这种变量要求一定等于某一个参数的值。如果该参数没有设置,就显示一个信息,然后退出,因此这种方式常用于出错指示。
    4、变量=${参数:+word}:如果设置了参数,则用word置换变量,否则不进行置换。

    字符串匹配的操作:


    ${PARAMETER#WORD} shell 像文件名扩展中那样扩展 WORD,并从 PARAMETER 扩展后的值的开头删除最短的匹配模式(若存在匹配模式的话)。使用 ‘@’ 或 ‘$’ 即可删除列表中每个参数的模式。 
    ${PARAMETER##WORD} 导致从开头删除最长的匹配模式而不是最短的匹配模式。 
    ${PARAMETER%WORD} shell 像文件名扩展中那样扩展 WORD,并从 PARAMETER 扩展后的值末尾删除最短的匹配模式(若存在匹配模式的话)。使用 ‘@’ 或 ‘$’ 即可删除列表中每个参数的模式。 
    ${PARAMETER%%WORD} 导致从末尾删除最长的匹配模式而不是最短的匹配模式。 
    ${PARAMETER/PATTERN/STRING} shell 像文件名扩展中那样扩展 PATTERN,并替换 PARAMETER 扩展后的值中最长的匹配模式(若存在匹配模式的话)。为了在 PARAMETER 扩展后的值开头匹配模式,可以给 PATTERN 附上前缀 #,如果要在值末尾匹配模式,则附上前缀 %。如果 STRING 为空,则末尾的 / 可能被忽略,匹配将被删除。使用 ‘@’ 或 ‘$’ 即可对列表中的每个参数进行模式替换。 
    ${PARAMETER//PATTERN/STRING} 对所有的匹配(而不只是第一个匹配)执行替换。 

    變數設定方式 str 沒有設定 str 為空字串 str 已設定非為空字串 
    var=${str-expr} var=expr var= var=$str 
    var=${str:-expr} var=expr var=expr var=$str 
    var=${str+expr} var= var=expr var=expr 
    var=${str:+expr} var= var= var=expr 
    var=${str=expr} str=expr
    var=expr str 不變
    var= str 不變
    var=$str 
    var=${str:=expr} str=expr
    var=expr str=expr
    var=expr str 不變
    var=$str 
    var=${str?expr} expr 輸出至 stderr var= var=str 
    var=${str:?expr} expr 輸出至 stderr expr 輸出至 stderr var=str


    [ian@pinguino ~]$ x="a1 b1 c2 d2"
    [ian@pinguino ~]$ echo ${x#*1}
    b1 c2 d2
    [ian@pinguino ~]$ echo ${x##*1}
    c2 d2
    [ian@pinguino ~]$ echo ${x%1*}
    a1 b
    [ian@pinguino ~]$ echo ${x%%1*}
    a
    [ian@pinguino ~]$ echo ${x/1/3}
    a3 b1 c2 d2
    [ian@pinguino ~]$ echo ${x//1/3}
    a3 b3 c2 d2
    [ian@pinguino ~]$ echo ${x//?1/z3}
    z3 z3 c2 d2

    字符串子集提取:
    ${x:3:5} 
    的值就是 “e val”,
    清单 9. shell 参数值的子字符串

    [ian@pinguino ~]$ x="some value"
    [ian@pinguino ~]$ echo "${x:3:5}"
    e val

    字符串长度:
    您已经知道 $# 表示参数的数量,而 ${PARAMETER:OFFSET:LENGTH} 扩展适用于单个参数以及 $* 和 $@,因此,可以使用一个类似的结构体 ${#PARAMETER} 来确定单个参数的长度也就不足为奇了。清单 10 中所示的简单的 testlength 函数阐明了这一点。自己去尝试使用它吧。
    清单 10. 参数长度
    [ian@pinguino ~]$ testlength () { for p in "$@"; do echo ${#p};done }
    [ian@pinguino ~]$ testlength 1 abc "def ghi"
    1
    3
    7

    1.4、运算和运算符

    1.4.1、整数运算符

    整数的算术运算符 
    + - * / % 
    赋值运算符 
    += -= * = / = %= 
    位运算符 
    << >> & | ~ ^ 
    位运算赋值运算符 
    << = >> = & = | = ~ = ^ = 
    逻辑运算符: 
    && || ! > > = < < = != ==

    expr命令计算一个表达式的值 
    格式 :expr arg
    例子:
    计算(2 +3 )×4 的值 
    1 、分步计算,即先计算2 +3 ,再对其和乘4
    s=`expr 2 + 3`
    expr $s * 4
    2 、一步完成计算: 
    expr `expr 2 + 3 ` * 4 
    说明: 
    运算符号和参数之间要有空格分开; 
    通配符号(* ), 在作为乘法运算符时要用 、“” 、‘’ 符号修饰

    关键字let计算表达式的值:
    #!/bin/bash
    x=2006
    let "x = $x + 1"
    echo $x
    x="a string."
    echo $x

    又出现了新的关键字:let。关于整数变量计算,有如下几种:" + - * / % ",他们的意思和字面意思相同,在*和/之前必须冠以反斜线,已防被SHELL先行解释。整数运算一般通过 let 和 expr 这两个指令来实现,如对变量 x 加 1 可以写作:let "x = $x + 1" 或者 x=`expr $x + 1`

    1.4.2、逻辑运算符

    对应操作 整数 字符串 
    相同 -eq = 
    不同 -ne != 
    大于 -gt > 
    小于 -lt < 
    大于或等于 -ge 
    小于或等于 -le 
    为空 -z 
    不为空 -n

    文件操作逻辑运算符:
    -d file ----当file是一个目录时,返回 True 
    -f file ----当file是一个普通文件时,返回 True 
    -r file ----当file是一个只读文件时,返回 True 
    -s file ----当file文件长度大于0时,返回 True 
    -w file ----当file是一个可写文件时,返回 True 
    -x "/bin/ls" ----当/bin/ls是一个可执行文件时,返回 True,目录是否可访问
    -e file ----当file存在时,返回True 
    -o file ----当file文件的所有者是当前用户时,返回True 
    -z file ----当file长度为0时,返回True 
    -u -----文件的 UID 标志被设置
    -G -----文件的组 ID 和当前用户相同
    file1 -nt file2 -----文件 file1 比 file2 更新
    file1 -ot file2 -----文件 file1 比 file2 更老


    逻辑连接符:
    ! expr 当expr的值是False时,返回True 
    Expr1 -a expr2 当expr1,expr2值同为True时,返回True 
    Expr1 -o expr2 当expr1,expr2的值至少有一个为True时,返回True

    命令逻辑连接符:
    [ -r "$mailfolder" ]||{ echo "Can not read $mailfolder" ; exit 1; } 
    使用{}把两个命令括起来,表示一个函数的用法。 && 与 ||或
    [ -f "/etc/shadow" ] && echo "This computer uses shadow passwors"
    注意:在“[”和“]”符号的左右都留有空格

    例子:
    #!/bin/sh
    mailfolder=/var/spool/mail/james
    [ -r "$mailfolder" ]||{ echo "Can not read $mailfolder" ; exit 1; }
    echo "$mailfolder has mail from:"
    grep "^From " $mailfolder
    其中 “^From“ 表示以 From 开头的


    1.5、流程语句
    1.5.1、if 语句

    if [ 逻辑表达式 ]; then    //这里需要注意:if和[之间必须有空格,[和逻辑表达式之间也必须有空格,表达式和]必须有空格
     #command code
    elif [ 逻辑表达式 ]; then
     #commandcode
    else
      #commandcode
    fi

    if [ expression ]
    then
    #code block
    elif [ expression ]
    then
    #code block
    else
    #code block
    fi 
    如果您为了简洁,想把 then 和 if 放在一行,那就要这样写了:if [ expression ]; then。即在 then 前加一个“;”号。

    1.5.2、case语句

    case string1 in 
    str1 ) commands1;; 
    str2 ) commands2;; 
    *) commands3;; 
    esac

    例子:
    #file lf.gz
    lf.gz: gzip compressed data, deflated, original filename,
    last modified: Mon Aug 27 23:09:18 2001, os: Unix
    脚本:
    #!/bin/sh
    ftype=`file "$1"`
    case "$ftype" in
    "$1: Zip archive"*) unzip "$1" ;;
    "$1: gzip compressed"*) gunzip "$1" ;;
    "$1: bzip2 compressed"*)bunzip2 "$1" ;;
    *) # * 通配符 代表其他
    error "File $1 can not be uncompressed with smartzip";;
    esac

    例子:
    #!/bin/bash 
    echo "Hit a key, then hit return."
    read Keypress

    case "$Keypress" in
    [a-z] ) echo "Lowercase letter";;
    [A-Z] ) echo "Uppercase letter";;
    [0-9] ) echo "Digit";;
    * ) echo "Punctuation, whitespace, or other";;
    esac 
    exit 0

    1.5.3、select 语句
    尤其擅长于交互式使用。用户可以从一组不同的值中进行选择。

    select var in ... ; do
     break
    done

    例子:
    #!/bin/sh
    echo "What is your favourite OS?"
    select var in "Linux" "Gnu Hurd" "Free BSD" "Other"; do
        break
    done
    echo "You have selected $var"

    下面是该脚本运行的结果: 
    What is your favourite OS?
    1) Linux 
    2) Gnu Hurd
    3) Free BSD
    4) Other
    #? 1
    You have selected Linux

    1.6、循环语句
    1.6.1、for语句

    for var in 数组列表; do
     #command bolock
    done

    例子1:
    #!/bin/bash
    for var in A B C ; do
     echo "var is $var"
    done

    例子2:
    #!/bin/sh
    #列出 RPM 的数目
    # 用法: showrpm rpmfile1 rpmfile2 ...
    # EXAMPLE: showrpm /cdrom/RedHat/RPMS/*.rpm
    for rpmpackage in $*; do
     if [ -r "$rpmpackage" ];then
      echo "== $rpmpackage =="
      rpm -qi -p $rpmpackage
     else
      echo "ERROR: cannot read file $rpmpackage"
     fi
    done

    例子3:
    for var1 in "$@" 
    do 
    #statements 
    done
    例2和例3的 $* 和“$@”是相同的

    1.6.2、while 语句
    while [ express ]; do
    #command
    Done

    例子1:
    count=1 
    while [ -n "$*"] 
    do 
    echo "this is a parameter number $count $1" 
    shift 
    count='expr $count + 1' 
    done

    例子2:
    while [ -n "$1" ]; do
    case $1 in
    -h) help;shift 1;; # function help is called 
    # 执行 help 函数 , shift 1 表示,移除第一个变量 $1 ,则第二个变为: $1 
      -f) opt_f=1;shift 1;; # variable opt_f is set
      -l) opt_l=$2;shift 2;; # -l takes an argument -> shift by 2
      --) shift;break;; # end of options
      -*) echo "error: no such option $1. -h for help";exit 1;;
      *) break;;
    esac
    done

    就像平常执行命令一样,当有参数-h 时,则执行相应的动作


    1.6.3、breake 和continue
    关键字"break" 用来跳出循环。而关键字”continue”用来不执行余下的部分而直接跳到下一个循环。

    1.6.4、shiftshift将存放在位置变量中的命令行参数,依次向左传递.例如 
    位置变量当前值为: 
    $1=file1 $2=file2 $3=file3 
    执行一次shift命令后,位置变量的值为: 
    $1=file2 $2=file3 
    还可以在shift命令中指定位置变量转移的次数, 如: 
    shift n 
    例子: 
    while [ "$1"] 
    do 
    if [ "$1"="-i"] then 
    infile=" $2" 
    shift 2 
    else if [ "$1"="-o"] then 
    outfile="$2" 
    shift 2 
    else 
    echo "Program $0 does not recognize option $1" 
    fi 
    done 
    tr a-z A-Z<$infile>$outfile


    二、函数
    脚本 b2d 将二进制数 (比如 1101) 转换为相应的十进制数。这也是一个用expr命令进行数学运算的例子:

    #!/bin/sh
    # vim: set sw=4 ts=4 et:
    help()
    {
     cat < b2h -- convert binary to decimal
    USAGE: b2h [-h] binarynum
    OPTIONS: -h help text
    EXAMPLE: b2h 111010
    will return 58
    HELP
     exit 0
    }


    error()
    {  # print an error and exit
      echo "$1"
      exit 1
    }

    lastchar()
    {  # return the last character of a string in $rval
      if [ -z "$1" ]; then
        # empty string
        rval=""
        return
      fi
      # wc puts some space behind the output this is why we need sed:
      numofchar=`echo -n "$1" wc -c sed ''s/ //g'' `
      # now cut out the last char
      rval=`echo -n "$1" cut -b $numofchar`
    }

    chop()
    {  # remove the last character in string and return it in $rval
      if [ -z "$1" ]; then
        # empty string
        rval=""
        return
      fi
      # wc puts some space behind the output this is why we need sed:
      numofchar=`echo -n "$1" wc -c sed ''s/ //g'' `
      if [ "$numofchar" = "1" ]; then
        # only one char in string
        rval=""
        return
      fi
      numofcharminus1=`expr $numofchar "-" 1`
      # now cut all but the last char:
      rval=`echo -n "$1" cut -b 0-${numofcharminus1}`
    }

    while [ -n "$1" ]; do
    case $1 in
      -h) help;shift 1;; # function help is called
      --) shift;break;; # end of options
      -*) error "error: no such option $1. -h for help";;
      *) break;;
    esac
    done

    # The main program
    sum=0
    weight=1
    # one arg must be given:
    [ -z "$1" ] && help
    binnum="$1"
    binnumorig="$1"

    while [ -n "$binnum" ]; do
      lastchar "$binnum"
      if [ "$rval" = "1" ]; then
        sum=`expr "$weight" "+" "$sum"`
      fi
      # remove the last position in $binnum
      chop "$binnum"
      binnum="$rval"
      weight=`expr "$weight" "*" 2`
    done
    echo "binary $binnumorig is decimal $sum"

    三、shell程序调试
    在编程过程中难免会出错,有的时候,调试程序比编写程序花费的时间还要多,Shell程序同样如此。  Shell程序的调试主要是利用bash命令解释程序的选择项。
    调用bash的形式是:  
    bash -选择项Shell程序文件名几个常用的选择项是:
      -e 如果一个命令失败就立即退出。
      -n 读入命令但是不执行它们。
      -u 置换时把未设置的变量看做出错。
      -v 当读入Shell输入行时把它们显示出来。
      -x 执行命令时把命令和它们的参数显示出来。

    四、信号
      trap命令用于在Shell程序中捕捉信号,之后可以有3种反应方式:
      (1)执行一段程序来处理这一信号。
      (2)接受信号的默认操作。
      (3)忽视这一信号。
      trap对上面3种方式提供了3种基本形式:
      第一种形式的trap命令在Shell接收到与signal list清单中数值相同的信号时,将执行双引号中的命令串。
      trap 'commands' signal-list
      trap "commands" signal-list
      为了恢复信号的默认操作,使用第二种形式的trap命令:
      trap signal-list
      第三种形式的trap命令允许忽略信号:
      trap " " signal-list
      注意:
      (1)对信号11(段违例)不能捕捉,因为Shell本身需要捕捉该信号去进行内存的转储。
      (2)在trap中可以定义对信号0的处理(实际上没有这个信号),Shell程序在其终止(如执行exit语句)时发出该信号。
      (3)在捕捉到signal-list中指定的信号并执行完相应的命令之后,如果这些命令没有将Shell程序终止的话,Shell程序将继续执行收到信号时所执行的命令后面的命令,这样将很容易导致Shell程序无法终止。
      另外,在trap语句中,单引号和双引号是不同的。当Shell程序第一次碰到trap语句时,将把commands中的命令扫描一遍。此时若commands是用单引号括起来的话,那么Shell不会对commands中的变量和命令进行替换,否则commands中的变量和命令将用当时具体的值来替换。

    五、bash中常用的命令Alias |设置命令别名 
    Bg |将一个被挂起的进程在后台执行 
    cd |改变用户的当前目录 
    exit |终止一个shell 
    export |使作为这个命令的参数的变量及其当前值,在当前运行的shell的子进程中可见 
    fc |编辑当前的命令行历史列表 
    fg |让一个被挂起的进程在前台执行 
    help |显示bash内部命令的帮助信息 
    history |显示最近输入的一定数量的命令行 
    kill |终止一个进程 
    pwd |显示用户当前工作目录 
    unalias |删除命令行别名

  • 相关阅读:
    Educational Codeforces Round 74 (Rated for Div. 2)
    Codeforces Round #591 (Div. 2, based on Technocup 2020 Elimination Round 1) 题解
    D
    Card Hand Sorting 二进制枚举暴力
    20172018-acmicpc-southeastern-european-regional-programming-contest-seerc-2017-en A
    Round #590 (Div. 3)
    A
    P2634 [国家集训队]聪聪可可
    HDU-4807-Lunch Time(二分+费用流,思维)
    易错分析
  • 原文地址:https://www.cnblogs.com/bugY/p/2420833.html
Copyright © 2020-2023  润新知