zoukankan      html  css  js  c++  java
  • shell编程中星号(asterisk "*")的坑

    今天分享一个有关shell编程中由通配符引起的问题。

    1. 问题代码

    cat test.logs

    4567890 *
    ##*************************************##
    rtyuio**tyuio432
    ##*************************************##
    *rtyuiop*2* yuiop
    ##*************************************##
    rtyuiop(3 * 4)iuytr
    ##*************************************##
    8765432
    

    cat script.sh

    #!/usr/bin/env bash
    
    # 主要功能是将 非##开头 的每行记录写入到文件中,每个文件保存一行记录
    logsname=test.logs
    i=100
    while read line
    do
        if [[ $line =~ '##' ]];then
            ((i++))
        else
            echo $line >> $i.txt
        fi
    done < "${logsname}"
    

    运行script.sh脚本的结果:
    问题运行结果

    从图片上红框部分可以看到:

    4567890 * 被替换为 4567890 script.sh test.logs
    rtyuiop(3 * 4)iuytr 被替换为 rtyuiop(3 100.txt 101.txt 102.txt script.sh test.logs 4)iuytr

    其他行都正常打印结果,为什么这两行会有问题呢?其他行也有星号,为什么没有被替换呢?

    2. 原因分析

    根据输出结果可以判断出问题的代码:echo $line >> $i.txt

    首先,介绍一下shell执行脚本的原理:

    1. shell读取整个脚本文件,然后从上到下依次执行每一行
    2. 假设当前line=4567890 *,当shell执行echo $line >> $i.txt
      1. 首先,shell负责替换$line的值为:4567890 *,此时代码是:echo 4567890 * >> $i.txt
      2. 然后,shell在执行echo命令之前,检查命令的参数中是否有通配符(PS:此时echo的参数:4567890 *)
      3. 很明显,*是通配符,shell负责解析通配符,shell会将通配符当作路径或文件名在磁盘上搜寻可能的匹配:若符合要求的匹配存在,则进行替换(路径扩展);否则就将该通配符作为一个普通字符参数传递给echo,然后再由echo进行处理。
      4. 解析完通配符后,*被替换为script.sh test.logs,此时echo命令的参数是:4567890 script.sh test.logs
      5. 最后,shell执行echo 4567890 script.sh test.logs,然后将echo命令执行的结果重定向到文件中。

    Tips:

    通配符看起来有点像正则表达式,但是它与正则表达式不同的,不能相互混淆。可以把通配符理解为shell能够处理的特殊字符。而且shell的通配符涉及的只有 "*, ?, [], {}" 这几种。

    通配符是shell自身支持的,而正则表达式需要相关工具的支持:grep,awk,vi,perl。在文本过滤工具里,都是用正则表达式,比如像awk,sed等,正则表达式是针对文件的内容通配符多用于文件名或者路径上,比如查找find,ls,cp等。

    3. 解决方案

    根据上面的分析可以知道,$line被替换后,通配符*再次被shell解析。那有什么办法可以防止shell解析通配符呢?

    1. 使用引用变量:"$line"在两端加上引号,这样"$line"就变成了一个字符串"4567890 *",而不是两个单独的字符串。
    2. 引用变量可防止分词和通配符扩展(也就是shell解析通配符),并且可以防止在变量中包含空格、换行符、通配符等时造成脚本中断。
    3. 在shell编程中总是使用引用变量的方式,这是一个良好又安全的编码习惯。

    4. 参考资料

    1. Reading asterisk character (*) from a file in bash
    2. When to wrap quotes around a shell variable?
    3. Security implications of forgetting to quote a variable in bash/POSIX shells
    4. shellcheck.net-finds bugs in your shell scripts.
    5. Linux Shell 通配符、元字符、转义符使用实例介绍
  • 相关阅读:
    反射泛型方法
    Redis令牌桶限流
    laravel中间件的使用
    Laravel-权限系统
    Laravel 即时应用的一种实现方式
    laravel实现多模块
    laravel5+ElasticSearch+go-mysql-elasticsearch MySQL数据实时导入(mac)
    swoole视频直播
    开发SSO单点登录需要注意的问题
    进程、线程、协程三者之间的联系与区别
  • 原文地址:https://www.cnblogs.com/wengle520/p/12459624.html
Copyright © 2011-2022 走看看