1、描述shell程序的运行原理(可附带必要的图形说明);
    shell是操作系统的外壳,为用户提供使用操作系统的接口,是命令语言、命令解释程序及程序设计语言的统称;是用户和Linux内核之间的接口程序,如果把Linux内核想象成一个球体的中心,shell就是围绕内核的外层,当从shell或其他程序向Linux传递命令时,内核会做出相应的反应。shell是一个命令语言解释器,它拥有自己内建的shell命令集,shell也能被系统中其他应用程序所调用。用户在提示符下输入的命令都由shell先解释然后传给Linux核心。如图:

linux运维实战练习-2015年9月13日-9月15日课程作业_awk

2、总结shell编程中所涉及到的所有知识点;

    变量:为程序在运行时在内存中申请的空间名字

        bash环境,可分为以下变量:

             本地变量:其作用域只在当前shell进程;

             环境变量:其作用域为当前shell进程及其子进程;

             局部变量:某个函数上下文,只存在于某个函数执行过程;

             位置参数变量:在脚本中引用传递给脚本的参数;在函数中引用传递给函数的参数;

             特殊变量:bash内建的用于保存某些特殊的值的相关变量,如:$?,$*,$@,$#等;

         变量类型:

            数值和字符类型

            变量类型的作用:决定存储空间,运算,定义存储格式

            语言对变量类型的支持力度:

                强类型:严格区分变量类型,变量在使用前必须先声明变量类型,如C等

                弱类型:不严格区分变量类型,一般变量有默认的类型,如bash,默认变量类型为字符型

            本地变量:变量名只能包含数字、字母和下划线;且不能以数字开头;

                             引用变量:$name,${name}

                             强引用:‘’

                             弱引用:“”

                             命令引用:`COMMAND`,$(COMMAND)

                             声明:declare,let

                             查看所有变量:set

                             生命周期:创建到销毁

                                                        自动销毁:shell进程终止

                                                        手动销毁:unset name

             环境变量:

                 被导出的本地变量:export name[=value]

                                                declare -x name[=value]

                 查看所有的环境变量:env,printenv,export

                 销毁:unset name

    脚本:实际上是运行一个bash进程,此进程负责从脚本文件中读取一个执行逻辑,而后由bash进程负责解析并运行此逻辑;

        启动脚本:

            (1)# bash 脚本文件位置

            (2)#./脚本文件,通过脚本文件第一行的shebang来确定解释器路径如:#!/bin/bash

         bash的常用选项:-n:解析脚本中的语法错误;-x:调试执行脚本

    命令状态结果:

         bash进程用于追踪执行的命令成功与否的状态:

             $?:上一条命令的执行状态结果,0为成功,1-255表示失败

         自定义脚本的状态结果:exit [n],在脚本中任何位置执行了exit命令即会终止当前shell进程

    条件测试:界定程序执行环境    
         整数测试:隐含着做数值大小比较,所以不要给变量引用加引用;
         $A -gt $B:是否大于;是则为“真”,否则为“假”;
         $A -ge $B: 是否大于等于;
         $A -lt $B:是否小于;
         $A -le $B: 是否小于等于;
         $A -eq $B: 是否等于;
         $A -ne $B:是否不等于;

     字符串测试:ASCII数值越大,字符比较时其值越大;
        "$A" > "$B":是否大于;
        "$A" < "$B":是否小于;
        "$A" == "$B":是否等于;
        "$A" != "$B":是否不等于;
        -z "$A":是否为空;空则为“真”,否则为“假”
        -n "$A":是否不空;不空则“真”,空则为“假”

     文件测试:测试文件的存在性以及属性;
        -e $file: 是否存在;存在则为“真”,否则为“假”;
        -a $file: 同上;
        -f $file:文件是否存在且为普通文件;
        -d $file:文件是否存在且为目录;
        -h $file:是否存在且为符号链接文件;
        -L $file: 同上
        -b $file:是否存在且为块设备文件;
        -c $file:是否存在且为字符设备文件;
        -S $file:是否存在且为套接字文件;
        -p $file: 是否存在且为管道文件;

        -r $file: 当前用户对文件是否拥有读权限;
       -w $file:当前用户对文件是否拥有写权限;
       -x $file:当前用户对文件是否拥有执行权限;

       -u $file:文件是否拥有SUID权限;
       -g $file:文件是否拥有SGID权限;
       -k $file:文件是否拥有sticky权限;

       -O $file: 当前用户是否为指定文件的属主;
       -G $file: 当前用户是否为指定文件的属组;

       双目操作符:
            $file1 -nt $file2: file1是否新于file2, file1的最近一次的修改时间戳是否晚于file2的;
            $file1 -ot $file2: file1是否旧于file2, file1的最近一次的修改时间戳是否早于file2的;
            $file1 -ef $file2:file1与file2是否指向了同一个inode;测试二者是否为同一个文件的硬链接;

       特殊设备:
            /dev/null: bit buckets,接受所有的输入数据,并直接丢弃;/dev/zero:吐出一堆0

    条件判断   

            if CONDITION; then
                if-true-分支
           else
                if-false-分支
            fi

            ! CONDITION: 取反

     与用户交互:

            read [options] VAR...

                      -p

                      -t

    命令引用:`COMMAND`,$(COMMAND0)

3、总结课程所讲的所有循环语句、条件判断的使用方法及其相关示例

    循环语句:

        for,while,until

        for VAR in LIST;do

               循环体

         done

          LIST生成方法:(1)整数列表

                                       {start..end},$(seq [start [step]] end)

                                       如:for i in {1..10}等于for i in $(seq 1 1 10)

                                 (2)直接给出列表:for i in 1 2 3 4 5 6 7 8 9 10

                                 (3)glob,如:for filename in /var/log/*

                                 (4)命令生成列表,如:for username in $(cut -d: -f1 /etc/passwd) 

    算术运算:
          +, -, *, /, %, **

        (1) $[$A+$B]
        (2) $(($A+$B))
        (3) let VARIABLE=$A+$B
        (4) VARIABLE=$(expr $A + $B)

    增强型赋值:+=,-=, *=, /=, %=,如:+=相当于

                         sum=$[$sum+$i]
                         let sum+=$i

    测试表达式:

          整数测试:-gt, -lt, -ge, -le, -eq, -ne
          字符串测试:==, >, <, !=, -z(字符串长度为0则为真), -n(字符串长度不为0则为真), =~(模式匹配,如:[[ "STRING" =~ PATTERN ]])

          组合测试条件:
                条件间逻辑运算:
                      与:多个条件要同时满足;
                      或:多个条件满足其一即可;
                      非:对指定的条件取反;

               表达式组合:
                      与:` CONDITION1 -a CONDITION2 `
                      或:` CONDITION1 -o CONDITION2 `
                      非:[ ! CONDITION ]

               命令组合:
                      与:COMMAND1 && COMMAND2  <-- [ EXPR1 ] && [ EXPR2 ]
                      或:COMMAND1 || COMMAND2
                      非:! COMMAND

               短路操作符:&&
                    false && true = false
                    false && false = false

                    true && false = true
                    true && true = true

                    if COMMAND1; then
                          COMMAND2
                    fi

              短路操作符:||
                   true || true = true
                   true || false = true

                   false || true = true
                   false || false = false

                     if ! COMMAND1; then
                         COMMAND2
                     fi

                  COMMAND1 && COMMAND2 || COMMAND3,相当于
                      if COMMAND1; then
                                 COMMAND2
                      else
                                COMMAND3
                      fi

    多分支if

              if CONDITION1; then
                     if-CONDITION1-true-分支
             elif CONDTION2; then
                   if-CONDITIO2-true-分支
               ...
             else
                   if-ALL-false-分支
             fi
    case语句
        简洁版多分支if语句;    
        使用场景:判断某变量的值是否为多种情形中的一种时使用;

        语法:
             case $VARIABLE in 
             PATTERN1)
                      分支1
                        ;;
            PATTERN2)
                      分支2
                        ;;
            PATTERN3)
                     分支3
                       ;;

            ...
            *)
                    分支n
                       ;;
            esac

          PATTERN可使用glob模式的通配符:
               *: 任意长度的任意字符;
              ?: 任意单个字符;
             []: 指定范围内的任意单个字符;
            a|b: 多选1;

    流程控制

        while循环:

        while CONDTION; do
                  循环体
        done

            进入条件:当CONDITION为“真”;
            退出条件:当CONDITION为“假”;

  

        unitl循环:
              until CONDITION; do
                        循环体
                        循环控制变量的修正表达式
              done

                        进入条件:当CONDITION为“假”时
                        退出条件:当CONDITION为“真”时

       循环控制:
              continue [n]:提前结束本轮循环,而直接进入下一轮;
              break [n]:提前结束循环;

      

     死循环:
         while true; do
                 循环体
                  if CONDTION; then
                          break
                  fi
         done

         until false; do
                  循环体
                  if CONDITION; then
                            break
                  fi
          done

       while循环的特殊用法:
           遍历文件的每一行:while read VARIABLE; do
                                                  循环体
                                          done < /PATH/FROM/SOME_FILE,变量为文件中的每一行

       for循环的特殊用法:
              for ((expr1;expr2;expr3)); do
                       循环体
             done

             如:for ((i=1;i<=100;i++))

    函数:
        function: 功能
             把一段具有独立功能代码封装在一起,并给予命名;后续用到时,可直接通过给定函数名来调用整体代码;

             函数作用:
                 代码重用;
                 模块化编程;

                 函数的使用方法:
                         先定义:编写函数代码
                         后调用:给出函数名,还可按需传递参数

                                        定义方法:
                                                   (1) function f_name {
                                                              函数体
                                                         }

                                                    (2) f_name() {
                                                               函数体
                                                          }

                                       调用函数:
                                                      f_name [argu1, argu2, ...]

                                       自定义函数状态返回值:
                                                          return [#]
                                                                   0: 成功
                                                          1-255:失败

                                        注意:函数代码执行时,一旦遇到return,函数代码终止运行,函数返回;


       模块化编程
               功能:把脚本文件中的代码分隔为多段,放在不同的文件中
               假设/root/bin/srv目录有两个文件:
                         (1) 函数文件
                        (2) 脚本文件

      为脚本使用配置文件
               一个文件中只定义变量
               脚本文件source此变量定义的文件

       变量的作用域:
               局部变量:
                   local VARIABLE=value

               存活时间:
                  函数执行开始,至函数返回结束;

4、总结文本处理工具sed及awk的用法;(必须附带示例)

     sed:行编辑器,每次读取文件符合模式条件的一行到模式空间,默认不编辑源文件,仅对模式空间的数据做处理,而后将模式空间内容打印至屏幕

     sed [options] 'AddressCommand' file ...
           -n: 静默模式,不再默认显示模式空间中的内容
           -i: 直接修改原文件
           -e SCRIPT -e SCRIPT:可以同时执行多个脚本
           -f /PATH/TO/SED_SCRIPT
     sed -f /path/to/scripts  file
           -r: 表示使用扩展正则表达式

            Address:
                  1、StartLine,EndLine,比如1,100
                        $:最后一行
                  2、/RegExp/,正则表达式来制定模式, 如/^root/
                  3、/pattern1/,/pattern2/,第一次被pattern1匹配到的行开始,至第一次被pattern2匹配到的行结束,这中间的所有行
                  4、LineNumber,指定的行
                  5、StartLine, +N, 从startLine开始,向后的N行;

            Command:
                  d: 删除符合条件的行;删除掉第5行至最后一行的内容

linux运维实战练习-2015年9月13日-9月15日课程作业_awk_02 

                  p: 显示符合条件的行;匹配到的行会显示(模式空间和命令处理结果都会显示)2次,如果不需要显示模式空间,需要在前面加-n如:

linux运维实战练习-2015年9月13日-9月15日课程作业_awk_03

linux运维实战练习-2015年9月13日-9月15日课程作业_shell_04

                   a \string: 在指定的行后面追加新行,内容为string,如在包含dev的下一行增加aaa

                       \n:可以用于换行

linux运维实战练习-2015年9月13日-9月15日课程作业_awk_05

linux运维实战练习-2015年9月13日-9月15日课程作业_awk_06

                  i \string: 在指定的行前面添加新行,内容为string
                  r FILE: 将指定的文件的内容添加至符合条件的行处

linux运维实战练习-2015年9月13日-9月15日课程作业_shell_07

                  w FILE: 将地址指定的范围内的行另存至指定的文件中;注意这会覆盖目标文件 

linux运维实战练习-2015年9月13日-9月15日课程作业_sed_08

           s/pattern/string/修饰符: 查找并替换,默认只替换每行中第一次被模式匹配到的字符串加修饰符

                                     g: 全局替换
                                     i: 忽略字符大小写
                                        s///: s###, s@@@,如果匹配中包含“/”,可用“#”或者“@”当分隔符 
                                          \(\), \1, \2分组
                                       &: 引用模式匹配整个串

linux运维实战练习-2015年9月13日-9月15日课程作业_shell_09

    awk:报告生成器,能够将文本中的信息已定义好的格式输出

           linux为GNU awk,默认以空格为分割符

           使用方法:

                  #awk [options] 'script' file1 file2...

           awk输出:

                 print:格式,print item1,item2

                 要点:
                        1、各项目之间使用逗号隔开,而输出时则以空白字符分隔,-F可以指定输入分割符,-OFS可以指定输出分割符;如图:

linux运维实战练习-2015年9月13日-9月15日课程作业_sed_10

linux运维实战练习-2015年9月13日-9月15日课程作业_awk_11

                        2、输出的item可以为字符串或数值、当前记录的字段(如$1)、变量或awk的表达式;数值会先转换为字符串,而后再输出;
                        3、print命令后面的item可以省略,此时其功能相当于print $0, 因此,如果想输出空白行,则需要使用print "";

                   awk变量:

                        awk内置变量--记录变量:

                        FS:输入字段分隔符,默认是空白字符;'{FS=""}'

                        RS:输入行分割符,默认是换行符;

                        OFS:输出字段分隔符,默认是空白字符;如:

                        ORS:输出行分割符,默认是换行符;如:

linux运维实战练习-2015年9月13日-9月15日课程作业_shell_12

                        awk内置变量--数据变量:

                        NR:awk所处理的记录数,如果有多个文件,则会把多个文件处理行统一技计数;

                        NF:统计当前处理行的字段总数

                        FNR:当前文件中正在处理的行数,如:

linux运维实战练习-2015年9月13日-9月15日课程作业_awk_13

                        用户自定义变量和赋值变量,如:

                        awk -v  a=“test”

                        awk '{a="test"}'


                  printf命令的使用格式:

                  printf:不会自动打印换行符,与print相比需要指定格式(format)

                  printf format item1,item2.....

                  format格式的指示符都%开头,后跟一个字符:
                      %c: 显示第一个参数的第一个字母;
                      %d, %i: 十进制整数;
                      %e, %E: 科学计数法显示数值;
                      %f: 显示浮点数;
                      %g, %G: 以科学计数法格式或浮点数格式显示数值;
                      %s: 显示字符串;
                      %u: 显示无符号整数;
                      %%: 显示%自身;

                  修饰符:
                      #:显示宽度
                       -:左对齐,默认为靠右对对齐
                      +:显示数值的符号
                      .#: 取值精度

                      如:

linux运维实战练习-2015年9月13日-9月15日课程作业_awk_14

                  awk的输出重定向

                  awk操作符:

                     算数操作符:+,-,*,/,等

                     字符串操作符:用于实现字符串连接

                     赋值操作符:=,+=,-=,--,++等

                  布尔值:在awk中任何非0或者非空字符串都为真,其余为假

                  比较操作符:<,<=,>,>=,==,!=,~(模式匹配),!~,subscript in array在某个数组中是否存在某个元素

                  逻辑操作符:&&,||

                  条件表达式:

                      selector?if-true-exp:if-false-exp,相当于以下语句

                      if selector;then

                          if-true-exp

                      else

                          if-false-exp

                      fi

                      如:比较两个数字的大小

linux运维实战练习-2015年9月13日-9月15日课程作业_shell_15

                  awk的模式:awk ‘programe’file1 file2 ..., 其中programe为 pattern { action }

                       常见的模式类型:

                       正则表达式:格式为/regular expression/,如:awk -F: '/^r/{print $0}' /etc/passwd

                       表达式:其值为非0或者非空是满足条件,如:

                             awk -F: '$3>1013{print $0}' /etc/passwd

                       指定的匹配范围:ranges,格式为pat1,pat2,如:

                             awk -F: '/^r/,/^l/{print $0}' /etc/passwd

                       BEGIN/END: 特殊模式,仅在awk命令的program运行之前(BEGIN)或运行之后(END)执行一次,如:awk -F: END'{print NF}' /etc/passwd,只显示最后一次的结果

                       如增加表头和表尾:

linux运维实战练习-2015年9月13日-9月15日课程作业_shell_16

                        空模式:匹配任意输入行

                        常见的Action

                         表达式

                         控制语句:

                           if-else格式:

                             if (condition) {then body} else {else body}

                             awk -F: '{if ($3>=500) {print $1,"is a common user"} else {print $1, "is an admin or system user"}}' /etc/passwd

                           while格式:

                             while (condition) {while body}               

                           do-while格式:do {do-while body} while (condition)

                           for格式:

                             for (variable assignment; condition; iteration process) {for body}

                             awk '{for (i=1;i<=NF;i+=2){printf "%s ",$i};print ""}' /etc/inittab

                             for循环可用来遍历数组元素:for (i in array) {for body}

                           case:

                           switch (expression) {case VALUE or /RGEEXP/: statement1;... default: stementN}

                           循环控制:continue,break

                           next:

                              提前结束对本行的处理进而提前进入下一行的处理;
                                   # awk -F: '{if($3%2==0) next;print $1,$3}' /etc/passwd
                                   # awk -F: '{if(NR%2==0) next; print NR,$1}' /etc/passwd

                  数组:

                      array[index-expression]

                      index-expression: 可以使用任意字符串; 如果某数组元素事先不存在,那么在引用时,awk会自动创建此元素并将其初始化为空串;因此,要判断某数组是否存在某元素,必须使用“index in array”这种格式                 

                      要遍历数组中的每一个元素,需要使用如下特殊结构:
                      for (var in array) {for body}, 其var会遍历array的索引;

                      如统计当前系统连接状态数量:

linux运维实战练习-2015年9月13日-9月15日课程作业_sed_17

               常用内置函数:
              split(string,array[,fieldsep[,seps]]):
              功能:将string表示的字符串以fieldsep为分隔符进行切片,并切片后的结果保存至array为名的数组中;数组下标从1开始;

             此函数有返回值,返回值为切片后的元素的个数

               # netstat -tn | awk '/^tcp/{lens=split($5,client,":");ip[client[1]]++}END{for (i in ip) print i,ip[i]}'

             length(string)
              功能:返回给定字串的长度

             substr(string,start[,length])
             功能:从string中取子串,从start为起始位置为取length长度的子串;

5、写一个脚本:如果某路径不存在,则将其创建为目录;否则显示其存在,并显示内容类型;(不要怀疑,就是这么简单)

        脚本:if [ ! -d $1 ];then
                    echo "$1 is not exist,creat directory $1"
                    mkdir $1
                    echo "done"
                 else
                     echo "directory is exist"
                     ls -l $1
                 fi

6、写一个脚本,完成如下功能;判断给定的两个数值,孰大孰小;给定数值的方法:脚本参数,命令交互;(使用read,依然如此简单)

         脚本参数:if [ $# -gt 0 ];then
                              if [ $1 -gt $2 ];then
                                 echo "$1 is greater than $2"
                             elif [ $1 -lt $2 ];then
                                 echo "$2 is greater than $1"
                             else
                                 echo "two parameters have the same value"
                              fi
                        else
                              echo "please input two parameters"
                              exit 8
                         fi

        命令交互:tip="Please input two number:"
                         read -p  "$tip" a b
                        (( $a )) && (( $b )) 2> /dev/null
                         if [ $? = 0 ];then
                            if [ $a -gt $b ];then
                               echo "a is greater than b"
                           elif [ $a -lt $b ];then
                                echo "b is greater than a"
                            else
                                echo "two parameters have the same value"
                            fi
                      else
                           echo "please input two interger number"
                      fi

7、求100以内所有奇数之和(至少用3种方法)

     for循环:
       sum=0
       for i in $(seq 1 2 100);do
              sum=$[$sum+$i]
       done
       echo $sum

     while循环:
          sum=0
          j=1
          while [ $j -lt 100 ];do
                sum=$(($sum+$j))
                    j=$(($j+2))
           done
           echo $sum

     until循环:

          sum=0

          k=1
          until [ $k -gt 100 ];do
                  sum=$(($sum+$k))
                  k=$(($k+2))
         done
         echo $sum

8、写一个脚本实现如下功能:

    (1) 传递两个文本文件路径给脚本;

    (2) 显示两个文件中空白行数较多的文件及其空白行的个数;

    (3) 显示两个文件中总行数较多的文件及其总行数;

linux运维实战练习-2015年9月13日-9月15日课程作业_sed_18

9、写一个脚本

  (1) 提示用户输入一个字符串;

  (2) 判断:

       如果输入的是quit,则退出脚本;

       否则,则显示其输入的字符串内容;

linux运维实战练习-2015年9月13日-9月15日课程作业_shell_19

10、写一个脚本,打印2^n表;n等于一个用户输入的值;

linux运维实战练习-2015年9月13日-9月15日课程作业_awk_20

11、写一个脚本,写这么几个函数:函数1、实现给定的两个数值的之和;函数2、取给定两个数值的最大公约数;函数3、取给定两个数值的最小公倍数;关于函数的选定、两个数值的大小都将通过交互式输入来提供。

linux运维实战练习-2015年9月13日-9月15日课程作业_sed_21

linux运维实战练习-2015年9月13日-9月15日课程作业_shell_22