zoukankan      html  css  js  c++  java
  • Shell编程规范:Don't use ls | grep

    一、背景

    最近在写一个私有化部署脚本的时候频繁的使用了ls和grep的组合,但是插件ShellCheck却一直在给我标黄,我这该死的洁癖终于在今天受不了了,这个使用了这么多年的好搭档怎么就那么不讨喜呢!

    ls | grep file
    ls | grep regexp
    ls | grep -v multi

    二、ShellCheck的规范

    很明显,提示告诉我们不要使用ls|grep的搭配,建议我们使用一个通配符或者带有条件的循环。。。

    一个ls都有这么多讲究,我们来看个wiki的使用反例:

    $ touch 'a space' $'a
    newline'
    $ echo "don't taze me, bro" > a
    $ ls | cat
    a
    a
    newline
    a space
    

    ls后面用管道符输出的结果怎么和预想的不一样??,我们来看看文件列表:

    $ ls -l
    total 8
    -rw-r-----  1 lhunath  lhunath  19 Mar 27 10:47 a
    -rw-r-----  1 lhunath  lhunath   0 Mar 27 10:47 a?newline
    -rw-r-----  1 lhunath  lhunath   0 Mar 27 10:47 a space
    

    Linux中的文件名是个神奇的存在,你可以使用几乎任意字符:空格、换行符、逗号、管道符等等。

    默认模式下,如果输出不是终端的话,ls会用换行符分割开文件名,所以问题就来了:从ls的输出中,您或计算机都无法知道它的哪些部分构成了文件名。是每个字吗?不。是每行吗?不。这个问题没有正确答案,只有:你不知道。

    哈哈哈,惊不惊喜?意不意外?当然,平时的使用其实都没有问题的,只是插件不建议我们这么用,那我就是改改我的使用吧,洁癖太可怕了。。。

    三、修改写法

    1、ls | grep file

    这个其实挺好改的,我的本意是想看下prometheus的目录权限、grafana目录下grafana.db文件的权限

    查一个目录的权限可以修改为(d指定只看目录):

    ➜  service-monitor git:(master) ✗ ls -dl prometheus
    drwxr-xr-x  17 Charles  staff  544  8 26 21:09 prometheus
    

    由于ls本身就有检索的功能,查看文件的权限就很简单了

    ➜  service-monitor git:(master) ✗ ls -l grafana/grafana.db
    -rw-rw-rw-  1 Charles  staff  2232320  8 26 22:36 grafana/grafana.db
    

    2、ls | grep regexp

    简化一下原有语句:

    for tarfile in $(ls images | grep '.tar$'); do echo "loading $tarfile"; done
    

    本意是想加载images文件夹下所有tar文件,先打印,后load镜像。

    我们知道ls后面的查询只能跟通配符,不能跟正则表达式,所以更改如下:

    ➜  build git:(master) ✗ ls images | grep '.tar$'
    monitor.tar
    ➜  build git:(master) ✗ ls images/*.tar
    images/monitor.tar
    

    然后for循环可修改为:

    ➜  build git:(master) ✗ file=$(ls images | grep '.tar$')
    ➜  build git:(master) ✗ echo $file
    monitor.tar
    ➜  build git:(master) ✗ file=images/*.tar
    ➜  build git:(master) ✗ echo $file
    images/*.tar
    ➜  build git:(master) ✗ for tarfile in images/*tar; do echo "loading $tarfile"; done
    loading images/monitor.tar
    

    3、ls | grep -v multi

    这个语句的本意是查询出当前目录下所有不是.sql或.zip结尾的文件
    由于ls不能用正则,也不能用反选,这个语句的修改一开始让我还很头疼,后来发现有个shell操作选项shopt,这个工具可以激活或关闭指定的shell行为选项。

    比如我现在要打开extglob模式,该模式可以给ls扩展匹配操作符,能使文件匹配更加方便. 不然不识别!

    开启命令:

    shopt -s extglob
    

    关闭命令:

    shopt -u extglob
    

    5个模式匹配操作符

    • ?(pattern-list) - 所给模式匹配0次或1次;
    • *(pattern-list) - 所给模式匹配0次以上包括0次;
    • +(pattern-list) - 所给模式匹配1次以上包括1次;
    • @(pattern-list) - 所给模式仅仅匹配1次;
    • !(pattern-list) - 不匹配括号内的所给模式。
    #反选删除文件:
    #(打开extglob模式)
    shopt -s extglob   
    rm -fr !(file1)
     
    #多个要排除的:
    rm -rf !(file1|file2)
    

    所以这时候我们的修改就很简单了

    shopt -s extglob
    rm -rf !(*.zip|*.sql) && ls -l
    

    四、最后

    改完之后心情豁然开朗,很多时候规范不只是有束缚编码行为的好处,还可以使你对代码使用有着更深的理解。

    五、参考

    ShellCheck Wiki: Don't use ls | grep
    ShellCheck Wiki: Iterating over ls output is fragile. Use globs.
    Why you shouldn't parse the output of ls
    Pattern Matching In Bash
    shopt
    linux extglob模式 和rm反选

  • 相关阅读:
    Ext.net中Combobox如何绑定数据库中的值-通用方法
    Tree通用的系列方法列表-treepanel
    这个时代“寒门再难出贵子” (转帖)
    java 文件保存到本地
    bootsrap的font awesome的各种图标,包括动画图标
    去除html的 标签
    Java从网络读取图片并保存至本地
    java正则表达式获得html字符串中<img src>的src中的url地址
    解决IDEA自动重置LanguageLevel和JavaCompiler版本的问题
    JAVA-JSP内置对象之page对象调用Servlet
  • 原文地址:https://www.cnblogs.com/mrcharleshu/p/13568693.html
Copyright © 2011-2022 走看看