bash脚本备忘

前端之家收集整理的这篇文章主要介绍了bash脚本备忘前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
  1. 语法基本介绍

    1.1 开头

    #!/bin/sh 
    程序必须以下面的行开始(必须方在文件的第一行): 
    符号#!用来告诉系统它后面的参数是用来执行该文件的程序。

    1.2 注释

    以#开头的句子表示注释

    1.3 变量

    "=" 左右两边都不能有空格; 
    语句结尾不需要分号
    引用变量进来使用${var}

    1.4 整数计算

    有如下几种:" + - * / % "
    在*和/之前必须冠以反斜线,以防被SHELL先行解释。
    一般通过 let 和 expr 这两个指令来实现
    如对变量 x 加 1 可以写作:
    let "x = $x + 1" 
    或者 x=`expr $x + 1`

    1.5 命令行参数

    $# 传入脚本的命令行参数个数; 
    $* 所有命令行参数值,在各个参数值之间留有空格; 
    $0 命令本身(shell文件名) 
    $1 第一个命令行参数; 
    $2 第二个命令行参数;

    1.6 局部变量

    变量首次被赋初值时加上 local 关键字就可以声明一个局部变量
    
    #!/bin/bash 
    
    HELLO="var1" 
    echo $HELLO 
    
    function hello 
    { 
        local HELLO="var2" 
        echo $HELLO 
    } 
    
    echo $HELLO 
    
    该程序的执行结果是: 
    
    var1 
    var2 
    var1

    1.7 BASH变量与C语言变量的区别

    BASH 中的变量在引用时都需要在变量前加上 "$" 符号( 第一次赋值及在For循环的头部不用加 "$"符号 ); 
    BASH 中没有浮点运算,因此也就没有浮点类型的变量可用; 
    BASH 中的整形变量的比较符号与 C 语言中完全不同,而且整形变量的算术运算也需要经过 let 或 expr 语句来处理;

    1.8 环境变量

    由export关键字处理过的变量叫做环境变量
  2. Shell命令和流程控制 (在shell脚本中可以使用三类命令)

    2.1 Unix 命令

    echo "some text": 将文字内容打印在屏幕上 
    ls: 文件列表 
    wc –l file; wc -w file; wc -c file: 计算文件行数 / 计算文件中的单词数 / 计算文件中的字符数 
    cp sourcefile destfile: 文件拷贝 
    mv oldname newname : 重命名文件或移动文件 
    rm file: 删除文件 
    grep 'pattern' file: 在文件搜索字符串比如:grep 'searchstring' file.txt 
    cut -b colnum file: 指定显示范围,输出到标准输出.如输出每行第5个到第9个字符 cut -b5-9 file.txt
    cat file.txt: 输出文件内容到标准输出设备(屏幕)上 
    file somefile: 得到文件类型 
    read var: 提示用户输入,并将输入赋值给变量 
    sort file.txt: 对file.txt文件中的行进行排序 
    uniq: 删除文本文件中出现的行列比如: sort file.txt | uniq 
    expr: 进行数学运算. Example: add 2 and 3 expr 2 "+" 3 
    find: 搜索文件比如:根据文件搜索find . -name filename -print 
    tee: 将数据输出到标准输出设备(屏幕) 和文件比如:somecommand | tee outfile 
    basename file: 返回不包含路径的文件名比如: basename /bin/tux将返回 tux 
    dirname file: 返回文件所在路径比如:dirname /bin/tux将返回 /bin 
    head file: 打印文本文件开头几行 
    tail file : 打印文本文件末尾几行 
    sed: sed是一个基本的查找替换程序。
        比如:将linuxfocus 替换为 LinuxFocus : 
        cat text.file | sed 's/linuxfocus/LinuxFocus/' > newtext.file 
    awk: awk 用来从文本文件提取字段。缺省地,字段分割符是空格,可以使用-F指定其他分割符。 
        cat file.txt | awk -F,'{print $1 "," $3 }'

    2.2 概念: 管道,重定向和 backtick

    2.3 使用反短斜线(` `)可以将一个命令的输出作为另外一个命令的一个命令行参数。

    命令: find . -mtime -1 -type f -print 
        用来查找过去24小时(-mtime –2则表示过去48小时)内修改过的文件。
        如果您想将所有查找到的文件打一个包,则可以使用以下脚本: 
    
    #!/bin/sh 
    # The ticks are backticks (`) not normal quotes ('): 
    tar -zcvf lastmod.tar.gz `find . -mtime -1 -type f -print`

    2.4 流程控制

    if ....; then 
    .... 
    elif ....; then 
    .... 
    else 
    .... 
    fi 
    
    通常用" [ ] "来表示条件测试。
    
    注意这里的空格很重要。要确保方括号的空格。 
    
    
    [ -f "somefile" ] :判断是否是一个文件 
    [ -x "/bin/ls" ] :判断/bin/ls是否存在并有可执行权限 
    [ -n "$var" ] :判断$var变量是否有值 
    [ "$a" = "$b" ] :判断$a和$b是否相等

    2.5 文件的操作: 含义( 满足下面要求时返回 TRUE )

    -e 文件已经存在 
    -f 文件是普通文件 
    -s 文件大小不为零 
    -d 文件是一个目录 
    -r 文件对当前用户可以读取 
    -w 文件对当前用户可以写入 
    -x 文件对当前用户可以执行 
    -g 文件的 GID 标志被设置 
    -u 文件的 UID 标志被设置 
    -O 文件是属于当前用户的 
    -G 文件的组 ID 和当前用户相同 
    
    {{{code 
        file1 -nt file2 文件 file1 比 file2 更新 
        file1 -ot file2 文件 file1 比 file2 更老 
    code}}}
    
    如 if [ -x /root ] 可以用于判断 /root 目录是否可以被当前用户进入 
    
    执行man test可以查看所有测试表达式可以比较和判断的类型。

    2.6 变量的比较操作

    相同      -eq         = 
    不同      -ne         != 
    大于      -gt         > 
    小于      -lt         < 
    大于或等于   -ge 
    小于或等于   -le 
    为空      -z 
    不为空         -n 
    
    比如: 
        比较整数 a 和 b 是否相等就写做 if [ $a = $b ] 
        判断整数 a 是否大于整数 b 就写做 if [ $a -gt $b ] 
        比较字符串 a 和 b 是否相等就写作:if [ $a = $b ] 
        判断字符串 a 是否为空就写作: if [ -z $a ] 
        判断整数变量 a 是否大于 b 就写作:if [ $a -gt $b ] 
        注意:在“[”和“]”符号的左右都留有空格。

    2.7 快捷操作符

    熟悉C语言的朋友可能会很喜欢下面的表达式: 
    [ -f "/etc/shadow" ] && echo "This computer uses shadow passwors" 
    这里 && 就是一个快捷操作符,如果左边的表达式为真则执行右边的语句。 
    
    {{{code
        #!/bin/sh
        mailfolder=/var/spool/mail/james 
        [ -r "$mailfolder" ] || { echo "Can not read $mailfolder" ; exit 1; } 
        echo "$mailfolder has mail from:" 
        grep "^From " $mailfolder 
        该脚本首先判断mailfolder是否可读。
        如果可读则打印该文件中的"From" 一行。
        如果不可读则或操作生效,打印错误信息后脚本退出。
    
        这里有个问题,那就是我们必须有两个命令: 
            -打印错误信息 
            -退出程序   
    
        我们使用花括号以匿名函数的形式将两个命令放到一起作为一个命令使用。
        一般函数将在下文提及。
        不用与和或操作符,我们也可以用if表达式作任何事情,但是使用与或操作符会更便利很多。  
    code}}}

    2.8 case
    case :表达式可以用来匹配一个给定的字符串,而不是数字。

    case "$var" in 
        condition1 ) do_thing_1;;
        condition2 ) do_thing_2;;
        * ) 
        default statments;; 
        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 
    
    该脚本可以自动解压bzip2,gzip 和zip 类型的压缩文件: 
    
        #!/bin/sh 
        ftype=`file "$1"` 
        case "$ftype" in 
        "$1: Zip archive"*) 
        unzip "$1" ;; 
        "$1: gzip compressed"*) 
        gunzip "$1" ;; 
        "$1: bzip2 compressed"*) 
        bunzip2 "$1" ;; 
        *) echo "File $1 can not be uncompressed with smartzip";; 
        esac 
    
    您可能注意到我们在这里使用了一个特殊的变量$1。
    该变量包含了传递给该程序的第一个参数值。
    也就是说,当我们运行: smartzip articles.zip 时 $1 就是字符串 articles.zip

    2.9 selsect

    select 表达式是一种bash的扩展应用,尤其擅长于交互式使用。用户可以从一组不同的值中进行选择。 
    select var in ... ; do 
    break 
    done 
    
    .... now $var can be used .... 
    
        #!/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

    2.10 loop表达式:

    #while-loop
    while ...; do 
    .... 
    done 
    
    
    #for-loop
    for var in [list]; do 
    .... 
    done 
    
    # for-loop example
    
        #!/bin/sh 
    
        for var in A B C ; do 
        echo "var is $var" 
        done            
    
    # 下面是一个更为有用的脚本showrpm,其功能是打印一些RPM包的统计信息: 
    
        #!/bin/sh 
        # list a content summary of a number of RPM packages 
        # USAGE: 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 
    
    这里出现了第二个特殊的变量$*,该变量包含了所有输入的命令行参数值。
    如果您运行showrpm openssh.rpm w3m.rpm webgrep.rpm 
    此时 $* 包含了 3 个字符串,即openssh.rpm,w3m.rpm and webgrep.rpm.

    2.11 until

    until 循环的基本结构是:

    until [ condition is TRUE ] 
    do 
    #code block 
    done

    2.12 引号

    单引号更严格一些。它可以防止任何变量扩展。
    双引号可以防止通配符扩展但允许变量扩展。 
    还有一种防止这种扩展的方法,那就是使用转义字符——反斜杆: echo \$SHELL 这将输出:$SHELL
  3. 实例

    3.1 二进制到十进制的转换

    #!/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 
    } 
    
    #return the last character of a string in $rval 
    #
    lastchar() 
    { 
        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` 
    } 
    
    # remove the last character in string and return it in $rval 
    #
    chop() 
    { 
        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" 
    
    该脚本使用的算法是利用十进制和二进制数权值 (1,2,4,8,16,..),
    比如二进制"10"可以这样转换成十进制: 0 * 1 + 1 * 2 = 2 
    为了得到单个的二进制数我们是用了lastchar 函数。
    该函数使用wc –c计算字符个数,然后使用cut命令取出末尾一个字符。

    3.2 文件循环程序

    或许您是想将所有发出的邮件保存到一个文件中的人们中的一员,但是在过了几个月 
    以后,这个文件可能会变得很大以至于使对该文件的访问速度变慢。下面的 脚本rotatefile 
    可以解决这个问题。这个脚本可以重命名邮件保存文件(假设为outmail)为outmail.1, 
    而对于outmail.1就变成了outmail.2 等等等等... 
    
    #!/bin/sh 
    # vim: set sw=4 ts=4 et: 
    
    ver="0.1" 
    help() 
    { 
        cat < 
        rotatefile -- rotate the file name 
        USAGE: rotatefile [-h] filename 
        OPTIONS: -h help text 
        EXAMPLE: rotatefile out 
        This will e.g rename out.2 to out.3,out.1 to out.2,out to out.1 
        and create an empty out-file 
        The max number is 10 
        version $ver 
        HELP 
        exit 0 
    } 
    
    error() 
    { 
        echo "$1" 
        exit 1 
    } 
    
    while [ -n "$1" ]; do 
    
    case $1 in 
    
    -h) help;shift 1;; 
    
    --) break;; 
    
    -*) echo "error: no such option $1. -h for help";exit 1;; 
    
    *) break;; 
    
    esac 
    
    done 
    
    # input check: 
    
    if [ -z "$1" ] ; then 
    
    error "ERROR: you must specify a file,use -h for help" 
    
    fi 
    
    filen="$1" 
    
    # rename any .1,.2 etc file: 
    
    for n in 9 8 7 6 5 4 3 2 1; do 
    if [ -f "$filen.$n" ]; then 
    p=`expr $n + 1` 
    echo "mv $filen.$n $filen.$p" 
    mv $filen.$n $filen.$p 
    fi 
    
    done 
    # rename the original file: 
    if [ -f "$filen" ]; then 
    echo "mv $filen $filen.1" 
    mv $filen $filen.1 
    fi 
    echo touch $filen 
    touch $filen   
    
    这个脚本是如何工作的呢?
    在检测用户提供了一个文件名以后,我们进行一个9到1的循环。
    文件9被命名为10,文件8重命名为9等等。
    循环完成之后,我们将原始文件命名为文件1同时建立一个与原始文件同名的空文件
  4. 调试

    最简单的调试命令当然是使用echo命令。
    shell也有一个真实的调试模式。如果在脚本”strangescript” 中有错误,您可以这样来进行调试:
    sh -x strangescript
    这将执行该脚本并显示所有变量的值。
    shell还有一个不需要执行脚本只是检查语法的模式。可以这样使用:
    sh -n your_script
    这将返回所有语法错误

    关于bash在控制台下的快捷键

    ctrl+u 删除光标以前的所有字符 
    ctrl+d 删除光标以前的一个字符 
    ctrl+k 删除光标以后的所有字符 
    ctrl+h 删除光标以后的一个字符 
    ctrl+t 调换光标前两个字符的次序 
    ctrl+a 移动光标到最前面 
    ctrl+e 移动光标到最后面 
    ctrl+p 上一个命令 
    ctrl+n 下一个命令 
    ctrl+s 锁定输入 
    ctrl+q 解除锁定 
    ctrl+f 移动光标到后一个字符 
    ctrl+b 移动光标到前一个字符 
    ctrl+x 标记一个位置 
    ctrl+c 清除当前的输入
  5. 最基本的理论基础

    5.1 stdin(标准输入),stdout(标准输出),stderr(标准错误输出)(std=standard).

    1.重定向stdout到一个文件 
    2.重定向stderr到一个文件 
    3.重定向stdout到stderr 
    4.重定向stderr到stdout 
    5.重定向stderr到stdout中并且成为一个文件 
    6.重定向stderrandstdouttostdout 
    7.重定向stderrandstdouttostderr 
    
    在Linux中1代表标准输出,2代表'标准错误' 
    
    标准输出 
        这个例子将会使ls的显示结果重定向到一个文件中. 
        ls-l>ls-l.txt 
    
    标准错误 
        这个例子将会使grep命令在运行过程中出现的错误输出到一个文件中 
        grepda*2>grep-errors.txt

    5.2 管道

    为什么会用到管道 - 管道可以使你非常方便的将一个程序的结果转向到另外一个程序中。 
    
    一个sed的例子,这个例子使用了非常简单的管道功能: 
    
    ls-l|sed-e"s/[aeio]/u/g" 
    
    执行以下命令后:
        首先ls–l会先执行并且它会输出结果信息但是如果它的后面跟是一个管道符的话,那么它就会将结果重新定向到sed这个程序中,sed使用了替换功能,
        所以这个例子执行完会,会将ls–l结果中所有含有aeio的英文单词替换成单词u. 
        通过另外的方法实现ls–l*.txt 
        也许这种方法不同于ls–l*.txt,但是它避免了出现一条NoSuchfileOrDirectory这种信息。 
        ls-l|grep".txt" 
        当ls–l执行后,它会将程序结果输出到grep这个程序中,并且去匹配.txt这条信息。jiao

猜你在找的Bash相关文章