今天分享一个有关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 通配符、元字符、转义符使用实例介绍

shell编程中星号(asterisk "*")的坑的更多相关文章

  1. shell编程中的单/双 小括号, 中括号, 大括号

    linux shell中的变量类型?分字符串或者数字或者bool类型吗? 参考: http://www.cnblogs.com/nufangrensheng/p/3477281.html 不分! sh ...

  2. Shell编程中Shift的用法

    Shell编程中Shift的用法 位置参数可以用shift命令左移.比如shift 3表示原来的$4现在变成$1,原来的$5现在变成$2等等,原来的$1.$2.$3丢弃,$0不移动.不带参数的shif ...

  3. Shell编程中while与for的区别及用法详解【转】

    在shell编程中经常用到循环,常用的循环有for和while循环两种.while循环默认以行读取文件,而for循环以空格读取文件切分文件,本篇就结合现网的一些使用示例说说二者的用法和区别. 一.常用 ...

  4. Linux Shell编程中的几个特殊符号命令 & 、&& 、 ||

    https://blog.csdn.net/hack8/article/details/39672145 Linux Shell编程中的几个特殊符号命令 & .&& . || ...

  5. shell编程中的if语句

    if语句在任何编程中都是必不可少.至关重要的分支语句,shell也是如此,只不过各种编程中的方式和格式有点不太一样 shell编程中的if语句基本格式如下: if [ X$1 = XA ];then ...

  6. shell编程中如何执行oracle语句

    shell编程中如果向oracle中插入数据之类的,需要先把执行语句放到文件中,然后再@这个文件执行 有如下俩种方式供参考: SQL=`sqlplus user/pwd@orains << ...

  7. shell编程中的 三种结构: 条件if/选择结构case/循环for/while/until等结构 和 函数的用法

    shell 函数的使用 (md中, 列表本身是有格式的, 他要产生缩进, 其次,列表项和列表项之间, 可以留有一个空行, 是合法的, 允许的) shell函数,就是 就相当于一个命令来看待和处理的, ...

  8. [ SHELL编程 ] shell编程中数值计算方法实例

    SHELL编程中经常会涉及到数值计算,有时候对于这些计算命令使用场景容易忘记或者混淆,这里针对常用的命令做个总结.主要包括let.bc.expr.(())等. 1.let 使用格式:let 表达式,表 ...

  9. shell编程中用户输入处理(shell 04)

    shell编程中用户输入处理1.命令行参数2.脚本运行时获取输入 命令行参数 通过空格来进行分割的位置参数 :$+position $0,$1,$2 ....$0 :程序名$1,$2,$3 ... $ ...

随机推荐

  1. python之event事件

    同进程的一样,线程的一个关键特性是每个线程都是独立运行且状态不可预测.如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手.为了解决这些问题,我们需 ...

  2. Markdown 语法手册

    1. 斜体和粗体 使用 和 * 表示斜体和粗体. 示例: 这是 斜体,这是 粗体. 2. 分级标题 使用 === 表示一级标题,使用 - 表示二级标题. 示例: 1234567 这是一个一级标题=== ...

  3. js - 常用的继承

    零.序言 参考资料:JavaScript常用八种继承方案: 注:1.此篇笔记是站在上述资料的肩膀上的一篇小结: 2.阅读之前建议温习一下 js 中的 prototype 和 constructor:( ...

  4. c/c++ main 函数命令行参数的使用

    C程序最大的特点就是所有的程序都是用函数来装配的.main()称之为主函数,是所有程 序运行的入口.其余函数分为有参或无参两种,均由main()函数或其它一般函数调用,若调用的是有参函数,则参数在调用 ...

  5. HDU-3579-Hello Kiki (利用拓展欧几里得求同余方程组)

    设 ans 为满足前 n - 1个同余方程的解,lcm是前n - 1个同余方程模的最小公倍数,求前n个同余方程组的解的过程如下: ①设lcm * x + ans为前n个同余方程组的解,lcm * x ...

  6. rsync参数详解

    Rsync的参数详细解释 -v, --verbose 详细模式输出-q, --quiet 精简输出模式-c, --checksum 打开校验开关,强制对文件传输进行校验-a, --archive 归档 ...

  7. Win32下双缓冲绘图技术

    一:双缓冲原理 为了解决窗口刷新频率过快所带来的闪烁问题,利用双缓冲技术进行绘图.所谓双缓冲技术,就是将资源加载到内存,然后复制内存数据到设备DC(这个比较快),避免了直接在设备DC上绘图(这个比较慢 ...

  8. python中字典dic详解-创建,遍历和排序

    原文地址:http://www.bugingcode.com/blog/python_dic_create_sort.html 在python的编程中,字典dic是最典型的数据结构,看看如下对字典的操 ...

  9. 添砖加瓦:MySQL分布式部署

    1.集群环境  管理节点(MGM):这类节点的作用是管理MySQLCluster内的其他节点,如提供配置数据,并停止节点,运行备份等.由于这类节点负责管理其他节点的配置,应该在启动其他节点之前启动这类 ...

  10. CSS 技巧汇总

    CSS 选择符优先级 !important 声明>内联样式(style)>id 选择符(#id)>类选择符(.class)=伪类选择符(:hover )=属性选择符([attr] ) ...