2. 工作中的实例分享 之 for,awk,while的几个小技巧

时间:2021-12-13 07:36:58

【这里分享的知识可能需要有一点的Linux基础的,所有的案例不建议在生产环境下操作,最好自己准备测试机进行试练】

  从事Linux运维工作已经快一年了,期间学到了一些比较实用的小技巧,有些新手可能还不太清楚,今天在这里姑且分享一下,当然限于个人水平和精力,可能有些地方描述不够清楚,欢迎各位读者朋友们进行斧正,并提出改进意见,在此先行谢过。

  在进行实操之前,我们先拓展两个小知识点:

  (1)反引号``,它的作用是引用命令并获取命令执行的结果,如:
    a='hello'      # 给变量a(变量不用单独定义,这点区别于C语言)赋值, 注意此处为单引号,非反引号,切记 
    echo $a         # 输出hello , 而$可理解为C语言中的&(取地址符),意为获取变量的值

    b=`echo $a     # 引用a变量的内容,并赋值给b

    echo $b       # 结果为 hello
(2)IFS: 是bash的内部的域分隔符,IFS的默认值为空白(包括:空格,tab, 和新行),这个有兴趣的自己可以好好研究一下
(3) 正则里面的两个匹配次数记忆小技巧,大家试一下这个方法看看效果会不会比较好:      .  匹配一次任意一个字符      +  匹配 任意字符 1到无穷次 (+ 横竖都是1,联想到数字 1 )   *  匹配任意字符 0到无穷次 (* 看上去就像一个圆形的雪花,联想到 圆心(0,0)
总结:a.当然反引号并不局限于echo, 诸如ls,cat,pwd等Linux基本命令都是可以用的, 例如 ls `pwd`/filename 可以获取到当前目录下你所查询的文件的全路径;

   b.反引号``绝大多数等价于$(),如a=3,此时b=`echo $a`和b=$(echo $a)都得到3。众多网友说这两个命令等价,实际上并非如此。笔者所知有限,目前只能给一个实例:

    如c
=123,a=c ,现借助a把c的值赋给变量d, 两种方式获取的结果是不等值的: d1=`eval echo \$$a` 和 d2=$(eval echo \$$a),有兴趣的朋友自己去研究一下。

 

实例:

一. 批量删除sdb1,sdb2,sdc1,sdc2,sdd1,sdd2,sdd3分区(保留盘符sda*)

1.如果磁盘是挂载状态,请先行卸载,否则会因磁盘使用中导致删除分区失败。

  for x in `df -h |grep '/dev/sd[a-d]*' | grep -v sda|awk '{print $1}'`;do umount $x ;done
思路分解:(1)在for条件中借助,然后用-v 滤除含有sda的盘符,最后用awk获取第一列盘符信息

     (2)for循环内部执行 卸盘操作 (umount 需要root权限才能执行)

总结:(1) 正则有标准和扩展两种,扩展的功能更多(兼容基本正则),用egrep替代grep或用grep -E替代grep都可以切到扩展正则用法

   (2)[a-zD-F]表示a~d和D~F内的任意一个字母,[15m]表示 1或5或m中的任何一个, 如ls 1[5m] 则能同时过滤到15和1m两个文件。

    
      sd[a-z]*:表示匹配以sda~sdz为首的任意字符串 ,例如 sda  sdb2  sddc.  sdddc都能被匹配到
   (3)文件file.txt 的内容为:  
        a 1
        b 2
     ① awk '{print $1}' file.txt 获取file.txt的第1列数据; 而获取最后一列列的数据可用$NF代替$1即可,而此处只有两列,故$NF可替换为$2。

     ②细心的读者应该发现了awk后{}被一对单引号扩起来了,那么双引号是否可行呢?现在看一个例子:
  echo linux.com | awk -F'.' '{print $1}' =>  linux      # 单引号,$1为awk内置变量,会被识别为当前处理文本行的第一列
  echo linux.com | awk -F'.' "{print $1}" =>  linux.com     # 双引号,$1为bash环境下的一个变量,非awk内置变量,故未得到预期结果
  echo linux.com | awk -F'.' "{print \$1}" => linux      # 在此处进行转义后,将bash变量转义为awk语句的内部变量,OK了
 
   上面的例子②给我们在跳板机上使用awk的启示,如服务器主机名格式为192-168-1-63,如何获取到所有主机名字的最后字段?(借助awk)
   绝大多数朋友都能写出这个语句ssh IP "hostname|awk -F'-' '{print $4}' " ,答案是什么呢?自己去试一下吧   
   请看下面这两种方案,请认真比较一下跟上面的语句的差异点是什么:
   (I)
ssh IP "hostname|awk -F'-' '{print \$4}'" # 对$进行转义,使得当前的bash环境不会误判
   (II)
ssh IP "hostname"|awk -F'-' '{print $4}' # 将获取的结果放到本地的bash环境下直接执行
 

2.删除除了sda*以外的分区: 

  for x in `df -h |grep /dev/sd* |grep -v sda /dev/sd[b-z][0-9]| cut -c1-8 |sort -u`;do echo -e "d\nw" | fdisk $x ;done

思路:思路跟卸载盘的思路很像,但此处涉及 fdisk用法,故sdb2,sdb3需要以sdb的形式出现,此处用cut截取1-8个字符(如/dev/sdd)后用sort -u去重。后面用

   echo传递多行参数给fdisk命令,实现毋须手动确认实现分区删除。注意,echo 的-e参数作用是使用特殊字符(如换行符\n),d是 fdisk里面的 删除分区的命

     令,w是fdisk中保存的含义。具体的含义可以利用fdisk查看,例如执行完 fdisk /dev/sdb后,按下 m,即可查看命令的含义。 

总结:用for和while的方式都是可行的,不过个人更倾向于while,原因在于while read在处理一行数据时,不会因为有空格而有所变化,而for处理一行数据时,若

   行数据没有空格,效果同while,可一旦遇到有空格的情况,缺陷就出来,当然for和IFS组合的时候也能实现行处理跟while read的方式,下面来演示一下:

  [root@xuegod63 my]# cat tmpfile
  a    1
  b    2
  c    3
  [root@xuegod63 my]# while read line ;do echo $line ;done < tmpfile     # 注意这里的tmpfile是一个文件名 
  a 1
  b 2
  c 3
  [root@xuegod63 my]# for x  in `cat c`;do echo $x;done                  # 这里如果直接用 for,那么会把 空格作为分割行,这样就不能达到我们需要的效果了
  a
  1
  b
  2
  c
  3
  [root@xuegod63 my]# for x in `cat c`;do IFS='';echo $x;done        # 这个是修正版,通过 IFS 指定分割,就能避免这个问题
  a    1
  b    2
  c    3

   今天的分享到此结束,当然很多原理性的东西没有讲到,笔者专注的是使用技巧的分享,所以各位读者朋友们也不要太苛责了哈