Linux Shell高级技巧

一、在循环中使用管道的技巧:

      在Bash Shell中,管道的最后一个命令都是在子Shell中执行的。这意味着在子Shell中赋值的变量对父Shell是无效的。所以当我们将管道输出传送到一个循环结构,填入随后将要使用的变量,那么就会产生很多问题。一旦循环完成,其所依赖的变量就不存在了。
 

    /> cat > test8_1.sh
      #!/bin/sh
      #1. 先将ls -l命令的结果通过管道传给grep命令作为管道输入。
      #2. grep命令过滤掉包含total的行,之后再通过管道将数据传给while循环。
      #3. while read line命令从grep的输出中读取数据。注意,while是管道的最后一个命令,将在子Shell中运行。
      ls -l | grep -v total | while read line
      do
          #4. all变量是在while块内声明并赋值的。
          all="$all $line"
          echo $line
      done
      #5. 由于上面的all变量在while内声明并初始化,而while内的命令都是在子Shell中运行,包括all变量的赋值,因此该变量的值将不会传递到while块外,因为块外地命令是它的父Shell中执行。
      echo "all = " $all

      /> ./test8_1.sh
      -rw-r--r--.  1 root root 193 Nov 24 11:25 outfile
      -rwxr-xr-x. 1 root root 284 Nov 24 10:01 test7.sh
      -rwxr-xr-x. 1 root root 108 Nov 24 12:48 test8_1.sh
      all =



      为了解决该问题,我们可以将while之前的命令结果先输出到一个临时文件,之后再将该临时文件作为while的重定向输入,这样while内部和外部的命令都将在同一个Shell内完成。
   

  /> cat > test8_2.sh
      #!/bin/sh
      #1. 这里我们已经将命令的结果重定向到一个临时文件中。
      ls -l | grep -v total > outfile
      while read line
      do
          #2. all变量是在while块内声明并赋值的。
          all="$all $line"
          echo $line
          #3. 通过重定向输入的方式,将临时文件中的内容传递给while循环。
      done < outfile
      #4. 删除该临时文件。
      rm -f outfile
      #5. 在while块内声明和赋值的all变量,其值在循环外部仍然有效。
      echo "all = " $all

      /> ./test8_2.sh
      -rw-r--r--.  1 root root   0 Nov 24 12:58 outfile
      -rwxr-xr-x. 1 root root 284 Nov 24 10:01 test7.sh
      -rwxr-xr-x. 1 root root 140 Nov 24 12:58 test8_2.sh
      all =  -rwxr-xr-x. 1 root root 284 Nov 24 10:01 test7.sh -rwxr-xr-x. 1 root root 135 Nov 24 13:16 test8_2.sh



      上面的方法只是解决了该问题,然而却带来了一些新问题,比如临时文件的产生容易导致性能问题,以及在脚本异常退出时未能及时删除当前使用的临时文件,从而导致生成过多的垃圾文件等。下面将再介绍一种方法,该方法将同时解决以上两种方法同时存在的问题。该方法是通过HERE-Document的方式来替代之前的临时文件方法。

      /> cat > test8_3.sh
      #!/bin/sh
      #1. 将命令的结果传给一个变量    
      OUTFILE=`ls -l | grep -v total`
      while read line
      do
          all="$all $line"
          echo $line
      done <<EOF
      #2. 将该变量作为该循环的HERE文档输入。
      $OUTFILE
      EOF
      #3. 在循环外部输出循环内声明并初始化的变量all的值。
      echo "all = " $all

      /> ./test8_3.sh
      -rwxr-xr-x. 1 root root 284 Nov 24 10:01 test7.sh
      -rwxr-xr-x. 1 root root 135 Nov 24 13:16 test8_3.sh
      all =  -rwxr-xr-x. 1 root root 284 Nov 24 10:01 test7.sh -rwxr-xr-x. 1 root root 135 Nov 24 13:16 test8_3.sh


    

二、自链接脚本:

      通常而言,我们是通过脚本的命令行选项来确定脚本的不同行为,告诉它该如何操作。这里我们将介绍另外一种方式来完成类似的功能,即通过脚本的软连接名来帮助脚本决定其行为。
    文件名:test9.sh

#!/bin/sh
#basename命令将剥离脚本的目录信息,只保留脚本名,从而确保在相对路径的模式下执行也没有任何差异。
#通过sed命令过滤掉脚本的扩展名。
dowhat=`basename $0 | sed 's/\.sh//'`
echo $dowhat
#这里的case语句只是为了演示方便,因此模拟了应用场景,在实际应用中,可以为不同的分支执行不同的操作,或将某些变量初始化为不同的值和状态。
case $dowhat in
test9)
echo "I am test9.sh"
;;
test9_1)
echo "I am test9_1.sh."
;;
test9_2)
echo "I am test9_2.sh."
;;
*)
echo "You are illegal link file."
;;
esac

[root@wu 桌面]# ln -s test9.sh test9_2.sh
[root@wu 桌面]# ./test9_2.sh
test9_2
I am test9_2.sh.

三、编写一个更具可读性的df命令输出脚本

#!/bin/bash
      #1. $$表示当前Shell进程的pid。    
      #2. trap信号捕捉是为了保证在Shell正常或异常退出时,仍然能够将该脚本创建的临时awk脚本文件删除。
      awk_script_file="/tmp/scf_tmp.$$"
      trap "rm -f $awk_script_file" EXIT
      #3. 首先需要说明的是,'EOF'中的单引号非常重要,如果忽略他将无法通过编译,这是因为awk的命令动作必须要用单引号扩住。
      #4. awk脚本的show函数中,int(mb * 100) / 100这个技巧是为了保证输出时保留小数点后两位。
      cat << 'EOF' > $awk_script_file
      function show(size) {
          mb = size / 1024;
          int_mb = (int(mb * 100)) / 100;
          gb = mb / 1024;
          int_gb = (int(gb * 100)) / 100;
          if (substr(size,1,1) !~ "[0-9]" || substr(size,2,1) !~ "[0-9]") {
              return size;
          } else if (mb < 1) {
              return size "K";
          } else if (gb < 1) {
              return int_mb "M";
          } else {
              return int_gb "G";
          }
      }
      #5. 在BEGIN块中打印重定义的输出头信息。
      BEGIN {
            printf "%-20s %7s %7s %7s %8s %s\n","FileSystem","Size","Used","Avail","Use%","Mounted"
      }
      #6. !/文件系统/ 表示过滤掉包含Filesystem的行,即df输出的第一行。其余行中,有个域字段可以直接使用df的输出,有的需要通过show函数的计算,以得到更为可读的显示结果。
      !/文件系统/ {
          size = show($2);
          used = show($3);
          avail = show($4);
          printf "%-20s %7s %7s %7s %8s %s\n",$1,size,used,avail,$5,$6
      }
EOF
df -k | awk -f $awk_script_file
[wulei@bogon 桌面]$ ./dd
FileSystem              Size    Used   Avail     Use% Mounted
/dev/sda2             17.45G   2.47G  14.09G      15% /
tmpfs                503.26M    232K 503.03M       1% /dev/shm
/dev/sda1            290.51M  31.03M 244.47M      12% /boot


你可能感兴趣的:(Linux Shell高级技巧)