美文网首页
shell脚本(4)让文本飞

shell脚本(4)让文本飞

作者: 盖小聂 | 来源:发表于2019-05-26 20:18 被阅读0次

    目录
    1、通配符与正则表达式
    2、grep命令
    3、使用sed进行文本替换
    4、使用awk进行高级文本处理

    1、通配符与正则表达式
    名称 描述
    通配符 shell在path name expansion(路径名扩展)时使用,一般使用于文件名匹配,常用于find,ls,cp,mv等命令
    正则表达式 正则表达式用于匹配文本,在文本内容的文本过滤工具里,大都用到正则表达式,如vi,grep,awk,sed等

    两者的差异: 通配符和正则表达式点像,但是不要混淆。通配符可以理解为只有*,?,[],{}四种符号,正则表达式要复杂的多。*在通配符和正则表达式中有不同的含义,作为通配符表示任意长度的字符,用在正则表达式中表示任意长度的前面字符,不能单独使用。

    //匹配邮件的正则表达式
    [a-z0-9_]+@[a-z0-9]+\.[a-z]+ 
    //匹配所有的单词
    ( ?[a-zA-Z]+ ?) 
    //匹配IP地址
    [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} 
    
    2、grep命令

    用于文件中的文本搜索

    //搜索包含特定模式的文本行
    grep pattern filename 或者 grep "pattern" filename 
    //从stdin中读取数据
    echo -e "this is a word\n next line" | grep word 
    //单个命令对多个文件进行搜索
    grep "match_text" file1 file2 file3 …… 
    //grep命令匹配的文本默认会红色高亮显示,即使没有显示使用--color=auto;
    //egrep命令匹配的文本不会高亮显示,需要显示使用--color=auto参数才能高亮显示
    grep word filename --color=auto 
    
    参数 描述
    -E 正则表达式匹配
    -o 只输出文件中匹配到的文本内容
    -v 打印除包含match_pattern的之外的所有行
    -c 统计文件或者文本中包含匹配字符串的行数
    -n 打印出包含匹配字符串的行号
    -l 搜索多个文件匹配文本位于哪个文件
    -i 忽略样式中的大小写
    -f 把需要匹配的内容写在一个文件中用-f参数去匹配这个文件中的内容
    --inclue/exclude 在grep搜索中指定或排除文件
    -q grep的静默输出。有的时候我们不需要搜索出匹配的文本,我们只需要知道文本在文件中是否匹配,可以使用该参数,然后判断命令是否执行成功,如果文本匹配命令的执行返回值为0执行成功,如果返回为非0则表示不匹配。
    -A/B/C 打印出匹配文本之前或之后的行
    //正则表达式匹配
    grep -E "[a-z]+" filename
    或者 
    egrep "[a-z]+" filename
    
    //只输出文件中匹配到的文本内容,结果为line.
    echo "this is a line." | grep -o "[a-z]+\."
    //统计文件中匹配项的数量
    echo -e "1 2 3 4\nhello\n 5 6" | egrep -o "[0-9]" | wc -l 
    
    //打印除包含match_pattern的之外的所有行
    grep -v match_pattern filename
    
    //统计文件或者文本中包含匹配字符串的行数
    grep -c text filename
    
    //打印出包含匹配字符串的行号
    grep linux -n example1.txt example2.txt
    或者 
    cat example.txt | grep linux -n 
    
    //搜索多个文件匹配文本位于哪个文件,
    //-l返回匹配的文件列表,-L返回不匹配的文件列表
    grep -l linux example1.txt example2.txt
    
    //忽略样式中的大小写
    echo hello world | grep -i "HELLO"
    
    //当前目录递归查找"test_function()"所在的文件的行
    grep "test_function()" . -R -n
    
    //用grep匹配多个样式
    grep -e "pattern1" -e "pattern"
    echo this is a line of text | grep -e "this" -e "line" -o
    
    //把需要匹配的内容写在一个文件中用-f参数去匹配这个文件中的内容
    grep -f pattern_filesource_filename
    
    //当前目录中递归搜索所有的.c和.cpp文件
    grep "main()" . -r --inclue \*.{c,cpp}
    //在搜索中排除所有的README文件
    //如果需要排除目录可以使用--exclude-dir选项
    //如果需要从文件中读取所需的文件列表,使用--exclude-from FILE
    grep "main()" . -r --exclude "README"
    
    //grep的静默输出
    grep -q "\$match_text" \$filename
    if [ $? -eq 0 ] ; then
      echo "The text exists in the file"
    else 
      echo "The does not exist in the file" 
    fi
    
    seq 10 | grep 5 -A 3 //-A选项,打印出匹配文本之后的3行
    seq 10 | grep 5 -B 3 //-B选项,打印出匹配文本之前的3行
    seq 10 | grep 5 -C 3 //-C选项,打印出匹配文件前后的3行
    

    使用0值字节作为后缀的grep和xargs
    xargs用于将文件名作为命令行参数传递给命令,文件名的分隔符最好是采用0值字节作为分隔符,如果采用空格作为分隔符,如果文件名中包含空格,文件名会被当做两个参数来处理

    //为了指明文件名以0值字节作为终止符,需要在xargs中使用-0
    grep "test" file* -lZ | xargs -0 rm
    
    3、使用sed进行文本替换

    常用于文本替换

    sed 's/pattern/replace_string/' file 
    或者
    cat file | sed 's/pattern/replace_string/' //只会替换第一个匹配的文本
    cat file | sed 's/pattern/replace_string/g' //会替换匹配的所有文本
    cat file | sed 's/pattern/replace_string/Ng' //从第n处开始替换
    

    分隔符可以用:或者|,可以使用双引号,在双引号中可以使用变量

    sed 's|原字符|替换至的字符|' filename
    sed 's:原字符:替换至的字符:' filename
    
    参数 描述
    -i 默认情况下sed只会打印替换后的文本,不会更改源文件,如果需要更改源文件需要使用-i参数
    sed -i "" 's/原字符/替换至的字符/' filename //更改会作用于原文件
    

    sed -i命令在mac上使用后面需要跟一个字符串,用来做备份文件,备份文件的名称为原来的名字加上这个字符串的名字;如果这个字符串为空(“”),那就不备份该文件,但是这个空字符不能省略。

    高级用法

    //移除空白行
    sed '/^$/d' file
    
    //直接在文件中进行替换
    sed -i "" 's/pattern/replace_string/g' filename
    
    //已匹配字符串标记&。在sed中,我们可以用&标记匹配样式的字符串,
    //这样就能够在替换字符串时使用匹配的内容。
    echo this is an example | sed 's/\w\+/[&]/g'
    输出的结果:this is an example 不是预期的[this] [is] [an] [example]尴尬,不知道是什么原因
    
    //子串匹配标记(\1)。&代表匹配给定样式的字符串。
    //但我们也可以匹配给定样式的其中一部分。
    //\(pattern\)用于匹配子串,\1代表匹配的一个子串,\2代表匹配的第二个子串
    echo this is digit 7 in a number | sed 's/digit \([0-9]\)/\1/'
    输出的结果:this is 7 in a number
    echo seven EIGHT | sed 's/\([a-z]\+\) \([A-Z]\+\)/\2 \1/'
    输出的结果:seven EIGHT 不是预期的EIGHT seven 尴尬,不知道是什么原因
    
    //组合多个表达式
    sed 'expression' | sed 'expression'
    sed 'expression; expression'
    sed -e 'expression' -e 'expression'
    echo abc | sed 's/a/A/' | sed 's/c/C/' //结果AbC
    echo abc | sed 's/a/A/; s/c/C/' //结果AbC
    echo abc | sed -e 's/a/A/' -e 's/c/C/' //结果AbC
    
    //引用,sed表达式通常用单引号来引用,当需要在表达式中使用变量的时候可以采用双引号
    text=hello
    echo hello world | sed "s/$text/HELLO/" //输出结果HELLO world
    
    4、使用awk进行高级文本处理

    可以用单引号包含也可以用双引号包含,三个语句块都是可选的
    awk语句的执行过程:
    首先,执行BEGIN{commands}语句块中的语句;变量初始化、打印输出表格的表头等语句通常都可以写入BEGIN语句块中
    接着,从文件或stdin中读取一行,然后执行pattern{commands}。重复这个过程,直到文件全部被读取完毕 ;相当于循环体,每次从文件中读取一行都会执行这部分的命令;若不提供该语句块,则默认执行{print},即打印所读取的每一行。关于print,需要记住两件重要的事情:当print的参数以逗号分隔的时候,参数打印时则以空格作为定界符。在awk的print语句中,双引号是被当做拼接操作符使用的。
    最后,当读至输入流末尾时,执行END{commands}语句块;像打印所有行的分析结果这类汇总信息,都是在END语句块中实现的常见任务。

    awk 'BEGIN{ print "start" } pattern { commands } END{ print "end" }' file
    

    特殊变量

    变量名称 含义
    NR 表示记录数量,在执行过程中对应与当前行号
    NF 表示字段数量,在执行过程中对应于当前行的字段数
    $0 这个变量包含执行过程中当前行的文本内容
    $1 这个变量包含第一个字段的文本内容
    $2 这个变量包含第二个字段的文本内容
    参数 描述
    -v 借助选项-v,我们可以将外部值传递给awk
    getline函数 awk通常默认读取一个文件的所有行。如果只想读取某一行,可以使用getline函数
    //借助选项-v,我们可以将外部值传递给awk
    eg1:
    VAR=10000
    echo | awk -v VARIABLE=$VAR '{ print VARIABLE }'
    10000
    
    eg2:
    var1=luo; var2=hong
    echo | awk '{ print v1,v2 }' v1=$var1 v2=$var2
    luo hong
    
    //使用过滤模式对awk处理的行进行过滤
    awk 'NR < 5' //行号小于5的行
    awk 'NR==1,NR==5' //行号在1到5之间的行
    awk '/linux/' //包含样式linux的行(可以用正则表达式来指定模式)
    awk '!/linux/' //不包含模式为linux的行
    

    对目录中的所有文件进行文本替换

    //将所有.cpp文件中的Copyright替换成Copyleft
    find . -name *.cpp -print0 | xargs -I {} -0 sed -i "" 's/Copyright/Copyleft/g' {}
    

    我们使用find在当前目录下查找所有的.cpp文件,然后使用print0打印出以null字符(\0)作为分隔符的文件列表。(这可以避免文件名中的空格所带来的麻烦。)接着使用通道将列表传递给xargs,后者将对应的文件作为sed的参数,有sed对文件内容进行修改。

    补充

    1、shell脚本中$#, $0,$1,$2,$?的含义

    名称 含义
    $# 脚本的参数个数
    $0 脚本的名称
    $1 脚本的第一个参数
    $2 脚本的第二个参数
    $? 上一个执行命令的返回结果(最后运行的命令的结束代码,即返回值)
    练习题

    1、统计特定文件中的词频
    2、解析文本中的电子邮件地址和URL
    3、在文件中移除包含某个单词的句子

    相关文章

      网友评论

          本文标题:shell脚本(4)让文本飞

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