zoukankan      html  css  js  c++  java
  • bash编程总结

    bash应该是目前Linux上最流行的shell脚本解释程序了(还有个shell叫dash,我太讨厌这个东东了。),只要你在linux上工作,并且希望自己能够工作得更愉悦,那么你应该熟悉最基本的bash编程,因为它将给你的工作带来足够的幸福感。本文将总结一些我自己平时使用的bash基本编程知识,和大家分享,也便于自己查询。

    变量

    1、bash的变量名是区分大小写的,并且变量名首字符不能是数字。看的各种代码也不少了,说实话,我还真没见到谁的代码用数字开头的变量名,我认为即使语言允许,这样做的人也很少,除非你真的很特别。

    2、变量定义与赋值

    aaa=123

    这里需要注意定义变量时等号前后都不能有空格,必须紧靠着写。虽然等号后面有空格的情况,语法可能不会出错,但结果绝对是错误的。

    3、变量拼接

    bbb=${aaa}123

    很多时候,我们可能需要用一些变量、常量字符串等来拼接出一个新的变量,这时需要注意用来拼接的变量可能需要加上{},否则可能会出现变量识别错误从而找不到变量的情况。这种情况,我倾向于所有变量一股脑的全加上{}。

    4、local和export

    变量定义时还有两个常用的关键字——local和export。export在下文再说,定义局部变量的local,我却基本不用,等我使用的时候再来补上总结。

    变量的定义也就那么回事,不去咬文嚼字的吭细节的话,懂这么一点点就够用了。如果,你的目标是成为shell高手,那么就需要专业级的学习,linux系统中有不少庞大的shell可以学习。

    条件判断

    if条件表达中长涉及到的比较有字符串整数文件属性比较等。

    if语句格式有:

    if [ expr ] ; then
    
          do something
    
    fi
    
     
    
    if [ expr ] ; then
    
          do something
    
    else
    
          do something
    
    fi
    
     
    
    if [ expr ] ; then
    
         do something
    
    elif [ expr ] ; then
    
         do something
    
    else
    
         so something
    
    fi

    if语句和其他语言(c,Java)相比,是行不同但神似。then关键可以另起一行,那样条件表达式后的分号就可以省略了。这里最需要注意的是 " [ " 和 " ] "前后至少需要一个空格来分割。

    1、整数比较

    整数大小比较涉及的操作符有—— -lt、-le、-eq、-gt、-ge和 -ne。

    例子:

    a=1
    b=2
    
    if [ $a -lt $b ] ; then
        echo "a < b"
    else
        echo "a >= b"
    fi
    
    if [ $a -ne 3 ] ; then
        echo "a != 3"
    else
        echo "a == 3"
    fi
    
    
    if [ 1 -gt 3 ] ; then
        echo "1 > 3"
    else
        echo "1 <= 3"
    fi

    使用整数大小比较的6个操作符时,涉及到的两个操作数将会作为数值来处理,而不是字符串,即使你使用双引号将比较对象给引起来,也是如此。

    2、字符串比较

    字符串比较可能用到的操作符有—— =、!=、>、<、-n和-z。

    例子:

    s1=aaa
    s2=bbb
    
    if [ $s1 < $s2 ] ; then
        echo "$s1 < $s2"
    else
        echo "$s1 >= $s2"
    fi
    
    if [[ $s1 < $s2 ]] ; then
        echo "$s1 < $s2"
    else
        echo "$s1 >= $s2"
    fi
    
    if [ $s1 = $s2 ] ; then
        echo "$s1 == $s2"
    else
        echo "$s1 != $s2"
    fi
    
    if [ -n $s1 ] ; then
        echo $s1
    fi

    用于字符串大小比较的>和<两个操作符比较特别,在[ ]中书写时需要转义,否则可以使用[[ ]]来替代[ ]。不转义,>和<会被解释为IO重定向操作。其次,if语句体不能为空,必须至少做一件事情。

    操作符-n 用于判断字符串不为空,即长度不为0。-z判断字符串为空,即长度为0。

    3、文件属性判断

    文件属性判断涉及到的操作符比较多,如下:

    -e、-a:文件存在

    -d:是目录

    -f:是文件

    -r:可读

    -w:可写

    -x:可执行

    f1 -nt f2:f1比f2新

    f1 -ot f2:f1比f2老

    这些操作符的使用方法和字符串操作符基本一致,在这几个操作符中我最常用的还是-e和-f,其他用得较少。

    其实,除了这三类常用的判断以外,还应该有执行命令结果的判断也比较常用,本文将不予总结了。

    for循环

    常用的for循环有两种主要形式

    形式一

    for e in [list]
    
    do
    
        do something
    
    done

    这种格式的for常用来遍历一个list集合中的所有元素,并加以处理。比如:

    1、遍历一个目录中的所有文件

    for file in `ls dir`
    
    do
    
         echo $file
    
    done

    2、遍历一个给定的集合

    list="a b c d e f g"
    
    for e in $list
    
    do
    
        echo $e
    
    done

    for in格式在遍历集合时,其实是根据空白字符来分隔字符串,取得每个元素的,上例中的`ls dir`和$list得到的都是一个带空白字符的字符串。

    形式二

    for ((i = 0; i < n; i++))
    
    do
    
        do something
    
    done

    这一种for的形式和C语言基本一致,只是需要双括号罢了,它更擅长做确定次数的循环计算。比如:

    for ((i = 0; i < 10; i++))
    
    do
    
        echo $i
    
    done
    for (( ; ; ))
    
    do
    
        echo "aaaa"
    
    done

    没有计数器的for循环就是一个死循环的实现,这和C语言的写法也是一致的,真有亲切感。

    掌握了这点for循环语句,我们就可以做很多的事情了,基本足够我们玩了。

    while循环

    这鸟蛋,我平时基本不用,一切需要循环的地方都用for。在这里列举while循环的原因,就是想借它喷一下各种语言里的各种while, do while,还有什么until等。一个循环非得搞出各种不同的表达方式不可,这一点还是Golang做得最到位。语法糖太多,总有一天会腻死你。哈哈。

    case语句

    case语句相当于绝大多数语言里的switch语句。这玩意除了具备if-elif的功能外,还支持通配符,这个相当有用。我们直接看例子。

    例子:

    url=www.tmall.com
    
    case $url in
        www.taobao.com)  echo 1;;
        *.taobao.com)         echo 2;;
        *.tmall.com)             echo 3;;
        www.tmall.com)      echo 4;;
        *)                               echo 5;;
    esac

    上例中条件分支不光有常量字符串,还有含通配符的字符串,这一点用来进行模式匹配非常便利。其次,需要注意case语言在匹配的过程中是从第一个开始逐一匹配,所有上例的输出结果是3,而不是精确匹配的4。我认为这算一个小小的遗憾,要是支持精确匹配优先就更好玩了。

    其次,需要注意的是每个条件分支体的结束必须用双分号。

    好了,case语言比较简单,但很实用。

    函数

    函数定义

    function hello()
    
    {
    
          echo "hello world"
    
    }

    函数定义实用关键字function,函数名后面的括号可有可无。

    函数调用

    无参数的函数调用只需要给出函数名就ok了,上面定义的函数直接用hello调用即可。有参数的函数,只需要将参数依次在函数名后面给出即可,如:func_name arg1 arg2。

    函数参数

    函数调用的时候可以给函数传递参数,那么函数体中又如何获取这些参数呢? 函数参数的获取和脚本程序参数的获取一致,都是通过$1、$2等来取得。比如:

    function add()
    {
        n=$1
        m=$2
        echo $((n + m))
    }


    调用add 1 2,将输出3。

    在函数中,可以通过$#的值来判断函数调用的时候,传递了几个参数。

    bash函数里很少使用return这种方式来返回值。不过可以这样调用函数来获取计算结果,比如:

    res=`add 1 2`

    变量res的值就是3了。注意上面不是单引号,而是数字1旁边的字符。

    字符串处理

    字符串处理差不多是整个计算机世界里做得最频繁的一件事情了。玩C语言的人很多事情都是在编写字符串处理程序。玩java的人大多数时候虽然不用自己去编写字符串处理程序,但也基本总是在调用字符串处理方法。linux其实有着非常强大的工具让我们去做字符串处理,不熟悉之前,每个工具貌似长得都非常复杂的样子。这里简单的总结一下自己使用过的字符串相关的东东。

    1、求子串

    str=abcdefg

    echo ${str:2:3} 将得到bcd,表达式中的2代表偏移量,3代表长度。

    2、求字符串长度

    str=123456

    echo ${#str}

    3、字符串替换

    ${变量/pattern/xx}   将变量中的第一个匹配替换为xx。

    ${变量//pattern/xx} 将变量中的所有匹配替换为xx。

    str=aaabbbccc

    echo ${str/aaa/xxx}

    echo ${str/a/x}

    有关字符串的处理,有着很多的工具,比如:你可以使用cut, awk等程序去拆分一个字符串等。

    整数运算

    整数运算一般使用$(( expr ))来进行。比如:

    echo $((1 + 2))
    a=1
    echo $((a + 2))
    echo $((a++))
    echo $((++a))
    b=2
    echo $(((a + b) / 2))

    可以看到只需要将计算表达式塞到$(())中就可以了,参与计算的变量并不需要$符号。整数运算涉及到的运算符有:++ 、--、+、-、*、/、%、+=、**(幂运算); 还有不常用的:<<、 >>、 ^、 &、 !、 |、 ~,这些在C语言中倒挺常用的,shell用来做这些位运算的需求也太2b了。 除了这些运算还支持逻辑:<、>、<=、>=、==、!=、&&、||。

    $(( expr ))表达式只能计算整数,不能用于小数的计算。如果,要做小数的计算,可以使用linux上的命令行计算器bc来完成。

    例子:

    echo "1.5 + 1" | bc

    代码生成

    程序员应该是”懒惰“的,”懒惰“的程序员可以创造出更多的自动化工具。我喜欢用bash来辅助我完成一些代码的自动生成,当然不是所有的代码都可以自动生成,如果那样也就不需要程序员了。我认为好的程序一定是可扩展的,最好的可扩展程序是”完成程序框架等开发后的功能需求开发阶段不再需要过分的写代码,只需要填代码“,能够填的代码,一定是可以自动生成的,这样的程序开发就将形成一个良性的循环。最近我非常热衷于重构我的代码,尽量让我的代码可以用bash脚本来自动生成,这将极大的提高我的开发效率。

    如何自动生成代码,生成什么样的代码,是根据自己的实际情况来完成的,这里没法介绍具体的代码生成,只能介绍bash用来完成代码生成的工具——cat。使用过linux的人都知道cat,这里就不详细说明了,只展示一段简单的bash脚本输出hello world程序。

    #!/bin/sh
    
     
    
    cat <<  END  > hello.c
    #include <stdio.h>
    
    int main(void)
    {
        printf("hello world
    ");
        return 0;
    }
    END


    在cat到END之间,你可以像在编辑器里一样编写自己的代码,这里写成什么样子,输出到源文件里就是什么样子,包括缩进等格式。当然,真正有意义的代码生成不会像这里的hello world这么简单,你必须得根据自己的实际情况去”拼凑“出自动化的代码。这里仅仅是展示使用cat来生成一个源码文件而已。要想自动生成真正有意义的,能提高自己开发效率的代码,必须得学习绝大多数的bash编程知识。

    source(.)与export

             使用linux的人都知道,我们敲打命令的终端其实就是运行在一个shell里的,这个shell里定义了很多的环境变量,比如:PATH,HOME等。然后,我在终端运行一个bash脚本程序后,这个bash脚本其实是运行在终端所在的shell fork出来的一个子shell进程里。那么这个子shell为什么也能够取得PATH,HOME这样的环境变量呢?这就是因为这些变量在其父shell里被声明为了export。export指令可以让变量传递到子shell里,这在一个bash程序调用另外一个bash程序时,共享变量是非常的有用的。

            export可以让变量传递到子shell里,但却不能让子shell的变量传递到父shell里,写到这里,我觉得我这个转折句说的很屁话。不过,bash中可以使用source命令来完成类似的功能。比如:我们可以在终端里用source来运行一个bash程序,那么这个程序中定义的变量,在程序结束后,依然存在。这是因为,通过source来执行一个脚本,并不会去fork出一个子shell来执行,而是直接在当前的shell里执行脚本中的所有语句。说到这里,我想你也会和我一样认为,source就相当于C语言中的include指令。

            当一个脚本过大时,我热衷于将它模块化,然后将一些模块独立出来作为一个单独的文件,再在主文件中通过source指令来执行之。如果,你和我一样的懒,懒得连source都不想写,那你完全可以使用点号(.)来替代source。

    逐行读文件

    当你在做文本处理的时候,可能需要将一个文件的内容一行一行的依次读出,然后加以处理,bash做这个事情是非常的方便的。

    例子:

    while read line
    do
        echo $line
    done < txt.log

    例子展示的是从文件txt.log中逐行的读出来赋值给变量line。如果,你的文件内容是结构化的,比如:每行都是两列,你想单独的处理每一列的内容,那么bash提供了更友好的方式去逐行读文件。

    例子:

    while read c1 c2

    do

        echo $c1 : $c2

    done

    这样read文件,第一列就赋值给了c1,第二列就赋值给了c2。这有点ruby,Python,go等优秀语言支持的多变量赋值的味道。

    格式化输出

    bash中最常用的输出莫过于echo了,可是echo不能格式化的输出,在自动生成代码这样的情况下,我想你有可能需要格式化输出,让代码更加的整齐美观。C语言有函数printf支持格式化输出,linux也有printf工具,让你如C语言一般随心所欲的输出。

    printf的大致用法举例:

    printf "my name is %s, I am %d years old. "  skoo 25

    我想linux printf的使用和C语言几乎没太大的区别,详细的用法可以man printf去。

    附:sed和awk

    我想sed和awk,绝对是两大神器,不过真正能驾驭它们的人并不多。我也只是略知皮毛,因此就不在这里废话了。学习任何一门编程语言,到体现生产力的时候,都是拼库。bash没有库函数的说法,但却有着丰富的linux命令,要写出真正强大有意义的bash程序,不得不学习一些linux命令,只有掌握了足够的linux命令,才能驾轻就熟。就像大多数java程序员一样,没有丰富的库,只掌握语法的他们也许什么有意义的事情都干不了。C/C++程序员也不例外,虽然他们热衷于用仅有的一点语法去重造轮子。

  • 相关阅读:
    jQuery Ajax学习
    jquery 学习
    jquery after append appendTo三个函数的区别
    rtmp服务器以及rtmp推流/拉流/转发
    Python字符编码详解
    Python自省(反射)指南
    Python线程指南
    Python正则表达式指南[转载]
    Python2.7 threading模块学习
    python中if __name__ == '__main__': 的解析
  • 原文地址:https://www.cnblogs.com/LUO77/p/5598282.html
Copyright © 2011-2022 走看看