shell笔记

作者: 郭苒 | 来源:发表于2018-04-12 18:54 被阅读0次

    背景

    在工作中,难免会使用shell处理一些繁杂的事情,比如图片的筛选分类等,但是在书写脚本时一不小心就会出问题,下面,记录一下容易出问题的地方。


    内容

    1 路径带空格

    比如说一个文件夹里面都是图片,你要把图片删除掉,不知道是谁脑残的在命名图片的时候加上了空格,比如我们其中一张图片的名字叫小花的 照片.PNG,而你的代码是这样的,

    #!/bin/bash
    for p in ./*;do
        rm $p
    done
    

    那么你在写如下代码遍历文件夹时,一定会遇到下面的错误

    rm: ./小花的: No such file or directory
    rm: 照片.PNG: No such file or directory
    

    怎么会出现这样的错误呢,我想你一定猜到了,对,是空格,空格把这个图片的名字分成了两部分,shell在读取字符串的时候,会以这样的形式去读取:

    rm 小花的 照片.PNG
    

    其实相当于rm 小花的rm 照片.PNG,那怎么办呢,我们必须让shell 指令读取整个字符串,而不是把后面的参数认为是多个文件名,最简单的方法是用双引号把我们的变量引起来。这里我们延伸一下,在shell字符串中,有两种方式表示字符串:

    1 单引号引起来:
    a、 单引号的任何字符都会原样输出,单引号里面的变量无效
    b 、单引号里面不能出现单个单引号(必须成对出现)
    2 双引号引起来
    a 、双引号里面可以有变量
    b、双引号里面可以出现转义符

    通过上面的解释,我们知道了,首先$p是变量,如果你用单引号括起来,那么我们的$p就会变成了$p字符串,已经再在是代表着小花的 照片.PNG的变量,用双引号就不会出现问题。至于说更复杂的做法这里不推荐,毕竟脚本就是为了简化。那么更改后的脚本时这样的:

    #!/bin/bash
    for p in ./*;do
        rm "$p"
    done
    

    2 比较浮点数的大小

    在工作种虽然遇到的不多,但是遇到了才发现自己还是有很多东西需要掌握的啊。比如,在工作中要从一个日志文件中筛选出得分在0.9以上的图片,那么我们就要比较得分和0.9的关系了,于是我自信的写下了以下代码:

    #!/bin/bash
    6 logFile="/Users/lilei/Desktop/logtxt"
    7  while read line;do
    8    score=$($line##*==)
    9    if [ $score >0.9 ];then
    10      echo $line
    11    fi
    12 done<$logFile
    

    但是,却报这样的错误……

    calculate.sh: line 9: 0.9: No such file or directory
    

    也就是说if判断这句除了问题,
    那么,我们如何进行浮点数判断呢,查资料后发现原来虽然bash内置了对四则运算的支持,但是并不支持浮点运算,mygod,强大的bash竟然不支持浮点数运算,那么我该怎么办???
    经过一番google发现大概有两种方法:

    2.1 利用bc神器

    bc其实是linux下计算语言,其完整的名称是An arbitrary precision calculator language,能够帮助我们进行各种精度的数据运算,当然对于浮点数的计算也不在话下,其正确使用方式如下:

    #!/bin/bash
    
    6 logFile="/Users/lilei/Desktop/logtxt"
    7  while read line;do
    8    score=$($line##*==)
    9    if [ `expr $score >0.9|bc` =1 ];then
    10      echo $line
    11    fi
    12 done<$logFile
    
    

    我们可以很清楚的看到上述表达其实是对括号内的表达式进行了一个运算,然后判断是否为真。

    2.2 利用awk

    对于awk的用法,自己可以百度脑补,下面我们来看看如何用它来判断浮点数的大小

    #!/bin/bash
    
     logFile="/Users/lilei/Desktop/logtxt"
      while read line;do
        score=$($line##*==)
        result=$(echo | awk -v x=$score '{if(x>0.9){print 1}else{print 0}}')
        if [ $result -eq 1 ];then
          echo $line
        fi
     done<$logFile
    

    当然上述awk的写法不止一种,以上只是其中一种……

    3 坑爹的if判断

    如果你第一次接触shell,你肯定会对if要和[[[之间留空格,[[[要和表达式之间留空格而想摔杯子,我也是,真觉得这种设计实在太变态,它会让我们经常因忘记加空格而出错,还有,如果你接触过shell,那么你知道[][[]]的区别吗?那么,我们接下来就来说说。
    如果我们要判断一个路径是不是一个真实存在的路径,那么我们用[]会这样写:

    if [ -d "$dir" ];then
        echo $dir
    fi
    

    为什么要加上双引号呢,前面第一条说过,那么要是用[[]]怎么写呢,看如下:

    if [[ -d $dir ]];then
        echo $dir
    fi
    

    看到了吗," "去掉了。在stackoverflow上给出的答案是[[ ]]语法是对[ ]语法的改进,前者有许多后者不具有的特性,如上面所展示的,[[ ]]能够自动处理带空格的字符串,不用你去添加告诉它在什么范围内是字符串,当然,如果你要是硬要加双引号,那么也可以。
    另外,[[ ]]可以让你像c语言一样进行&&||等多条件判断,还可以用<>进行字符串或数字比较,例如:

    if [[ $a > 3 || $a < 10 ]];then
      echo $a
    fi
    

    但是[ ]却不行。
    另外,[[ ]]还提供了一种模式匹配的写法,通用语法if [[ $a = *patternStr* ]],例子如下:

    path="/Users/guoran/Desktop/"
    if [[ $path = *guoran* ]];then
      echo $path
    fi
    

    这里需要说明一点,上面等号后面的匹配字符不能加双引号,如果加了就会报语法错误(这个暂时还不知道是为什么?希望大神指教),等号可以写成===都可以,这基本是我最近写脚本时遇到的坑,记录一下。


    思考

    发现总是赖得记东西,总是以浪费时间,工作忙为借口来宽慰自己,但是发现不记东西,反而看过或写过的东西过了一段时间就又忘记了,从现在起,要强迫自己开始记录一下自己工作过程中遇到的问题,成长要从点滴做起,记住哈,少年!!!


    参考资料:
    https://stackoverflow.com/questions/3427872/whats-the-difference-between-and-in-bash
    https://unix.stackexchange.com/questions/306111/what-is-the-difference-between-the-bash-operators-vs-vs-vs/306115

    相关文章

      网友评论

        本文标题:shell笔记

        本文链接:https://www.haomeiwen.com/subject/rvrfkftx.html