Perl/Unix单线Cage Match,第1部分
2021年5月12日桑德普·阿加瓦尔
shell(如Bash)提供了内置命令和脚本功能,可以轻松解决和自动化各种任务。grep、sed、Awk、sort、find或parallel等外部命令可以组合使用。有时,您可以将Perl用作特定用例的单个替代或补充。
Perl是满足文本处理需求的最健壮的可移植选项。Perl有一个功能丰富的正则表达式引擎、内置函数、广泛的生态系统,并且非常便携。然而,与专用工具相比,Perl的性能可能较慢,并且可能更加冗长。
单线还是脚本?
对于数字信号处理(DSP)芯片的组装级测试,我必须为多个地址范围复制相同的场景。当时我对Linux命令行的工作知识有限,不知道如何使用sed或Awk。我使用Vim和Perl来满足各种文本处理需求。
我不知道Perl的单行程序选项,所以每当我必须替换多个文件时,我都会修改脚本。有一次,我甚至将文件作为Vim缓冲区打开,并应用了一个布夫多
命令,看看这是否会使我的工作流程更简单。如果我知道Perl一行程序,我就可以很容易地利用find和Bash globs来简化我的工作,例如:
$perl-i-pe的/0xABCD/0x1234;/;s/0xDEAD/0xBEEF;/’*.测试
这个-我
选项将把更改写回源文件。如果需要,我可以传递一个参数来创建原始文件的备份。例如,-i.bkp公司
将创建ip文本.bkp作为的备份ip.txt(ip.txt)作为输入文件传递。我还可以将备份放在另一个现有目录中。这个*
扩展为原始文件名:
$mkdir备份$perl-i'备份/*'-聚乙烯's/SEARCH/REPACE/g'*.txt文件
强大的regexp功能
Perl regexp比实用程序使用的基本或扩展正则表达式功能强大得多。我经常使用的共同特征是非贪婪和所有格量词、lookaround、/e(电子)
标志、子表达式调用和(*跳过)(*失败)
。以下是我多年来回答的StackOverflow线程的一些例子。
跳过一些匹配项
需要这个问题将avr-asm转换为arm-gnu注释。起始文件如下所示:
ABC r1,';'ABC r1,";"; 评论;;;
我需要改变;
到@
,但是;
在单引号或双引号内不应受到影响。我可以匹配报价;
在交替和使用的第一个分支中(*跳过)(*F)
不替换:
$perl-pe(美元)'s/(?:\x27;\x27|“;”)(*SKIP)(*F)|;/@/'ip.txt(ip.txt)ABC r1,';'ABC r1,";"@评论@;;
我使用(*跳过)(*F)
我经常希望它有一个更短的语法,(*平方英尺)
例如。
用递增值替换字符串
我可以用递增值替换字符串. The/e(电子)
在替换上,我可以将替换端视为Perl代码。无论该代码的计算结果是什么,都是替换代码。这可以是我增加的变量:
$回声“a a a a”|perl-聚乙烯's/*\|*/$i++/ge'a0a1a2a3a4a5a6a
反转子字符串
我还使用了/e(电子)
戏法反转与模式匹配的文本:
$回声'罗马789:qwerty12543'|perl-聚乙烯's/\d+$/reverse$&/e'罗马789:qwerty34521
做一些算术运算
添加另一个/e(电子)
得到/ee公司
意味着有两轮Perl代码。我对替换端进行求值,以获得将作为Perl代码求值的字符串。在文本文件中的算术替换,我需要找到简单的算术,比如25100+10
,并将其替换为其算术结果:
身份证件=25100+10xyz公司=1+美国广播公司=123456conf字符串=LMN、J、IP,25100+1,0,3,1
我可以用一个/e(电子)
通过匹配数字并在替换端执行一些Perl操作:
$perl-pe(美元)“s/(\d+)\+(\d+/$1+2/ge”ip.txt(ip.txt)身份证件=25110xyz公司=1+美国广播公司=123456conf字符串=LMN,J,IP,25101,0,3,1
但我可以匹配整个表达式,而不是单独匹配数字。比赛开始了$&
,所以第一个/e(电子)
将其插入到25100+10
。第二轮将其作为Perl运行,这是一个补充:
$perl-pe(美元)“s/\d+\+\d+/$&/gee”ip.txt文件身份证件=25110xyz公司=1+美国广播公司=123456conf字符串=LMN,J,IP,25101,0,3,1
这也将更容易处理一组运算符:
$回声'2+3 10-3 8*8 11/5'|perl-聚乙烯's|\d+[+/*-]\d+|$&|gee'
5 7 64 2.2
处理换行符
我想取消键入此文本:
你好。天要下雨了-天。有保险箱和愉快的jou-内尼。
与sed和Awk不同,您可以选择在Perl中保留记录分隔符。这样更容易解决这个问题:
$perl-pe(美元)'s/-\n//'消息.txt你好。今天会下雨。有保险箱和愉快的旅程。
请参见删除破折号并用空格替换换行符并将Perl解决方案与sed/Awk进行比较。
多行固定字符串替换
使用Perl中的内置功能,转义regexp元字符更简单。结合将整个输入文件转换为单个字符串,我可以轻松地执行多行固定字符串替换。考虑以下示例输入:
这是一条多行带批次的样本输入特殊字符的类似。()*[]${}^ + ?\和'等等。
假设您有一个包含要匹配的行的文件:
以及包含替换字符串的文件:
---------------------$&=$1 + $2 /三 \4=====================
以下是使用Perl的一种方法:
$perl-0777-内'$#ARGV==1$s=$:$#ARGV==0$r=$_:打印s/\Q$s/$r/gr'搜索.txt替换.txt ip.txt这是一条多行带批次的样本输入特殊字符的---------------------$&=$1 + $2 /三 \4=====================
注意,在上述溶液中搜索.txt
和替换.txt
也由Perl命令处理。避免使用shell变量保存其内容,因为尾部换行符和ASCII NUL字符需要特别注意。
Awk和sed没有相应的选项来吞咽整个输入文件内容。Sed是图灵完备的,Awk是一种编程语言,因此,除了转义元字符所需的代码外,如果您愿意,还可以为它编写代码。
更好的regexp支持
其他一些regexp库存在与实现它们所用的任何内容相关的问题。例如,GNU版本可能有一些其他实现可能没有的错误。您使用的版本可能会产生不同的结果。然而,Perl到处都有相同的错误。
反向引用
有一个glibc中的反向引用问题我找到的为grep报告。这个bug至少出现在grep和sed的GNU实现中。据我所知,Awk的任何实现都不支持regexp定义中的反向引用。
我想得到两次出现连续重复字符的单词。此示例花费了一些时间,结果没有输出:
$grep-xiE(美元)'([a-z]*([a-z])\2[a-z]*){2}'/usr/share/dict/words
展开嵌套或使用PCRE时,它会起作用:
$grep-xiE(美元)'[a-z]*([a-z])\1[a-z4]*([a-z])[2[a-z2]*'/usr/share/dict/words(usr/share/dict/words)雅培安娜贝利...$grep-xiP(美元)'([a-z]*([a-z])\2[a-z]*){2}'/usr/share/dict/words雅培安娜贝利...
这是Perl,它是原始的regexp:
$perl-ne(美元)'打印条件/^([a-z]*([a-z])\2[a-z]*){2}$/i'/usr/share/dict/words雅培安娜贝利...
单词边界
为什么这个sed命令不替换第三个到最后一个“and”?当涉及单词边界和组重复时,显示了另一个有趣的错误。使用glibc中的regexp代码可以看到此错误(就像在Linux上一样):
这不正确地匹配,因为“cocoa”中间没有单词边界:
$sed--版本塞德(逗号分隔符) 4.8$回声“可可”|sed-北欧'(\bco){2}/p'可可
没有量词,就没有问题,也没有匹配:
$回声“可可”|sed-北欧'/\bco\bco/p'$回声“可可”|perl-ne语言'如果/(\bco){2}/则打印'
这是GNU sed中的另一个示例。这会修改行,因为它认为“it”是在“with”之后两次作为单独的单词找到的,但第二次实际上位于“sit”的中间:
$回声“它和它排在一起,也坐在这里”|sed-电子's/with(.*\bit\b){2}/XYZ/'它也行XYZ
更改模式以去掉量词,它就会正常工作:
$echo(回声)“它和它排在一起,也坐在这里”|sed-电子's/with.*\bit\b.*\比特\b/XYZ/'它和它排成一线,也坐在这里$回声“它和它在这里排成一行,也坐在这里”|sed-电子's/with.*\bit\b.*\比特\b/XYZ/'它线XYZ#Perl不需要这样的解决方法$回声“它和它排在一起,也坐在这里”|perl-聚乙烯's/with(.*\bit\b){2}/XYZ/'它和它排成一线,也坐在这里$回声“它和它在这里排成一行,也坐在这里”|perl-聚乙烯's/with(.*\bit\b){2}/XYZ/'它线XYZ
敬请期待
我将在第2部分中介绍更多内容,在这里我将深入研究XML、JSON和CSV。
其他需要阅读的内容
[图像来自点心!在Flickr上,(CC BY-NC-ND 2.0)]