Perl 命令行多行替换

Posted

技术标签:

【中文标题】Perl 命令行多行替换【英文标题】:Perl command line multi-line replace 【发布时间】:2012-03-29 01:13:00 【问题描述】:

我正在尝试使用命令行 perl 替换多行文件中的文本。我正在使用 Ubuntu Natty。

以下是我的文本文件(称为 test.txt)的内容:

[mysqld]
#
# * Basic Settings
#

#
# * IMPORTANT
#   If you make changes to these settings and your system uses apparmor, you may
#   also need to also adjust /etc/apparmor.d/usr.sbin.mysqld.
#

user            = mysql
socket          = /var/run/mysqld/mysqld.sock
port            = 3306
basedir         = /usr
datadir         = /var/lib/mysql
tmpdir          = /tmp
skip-external-locking

下面是我的 perl 命令:

perl -i -pe "s/(\[mysqld\][^\^]+)/\1\nsometext/g" test.txt

但是,我最终得到的不是替换文件中的所有文本,而是:

[mysqld]

sometext#
# * Basic Settings
#

#
# * IMPORTANT
#   If you make changes to these settings and your system uses apparmor, you may
#   also need to also adjust /etc/apparmor.d/usr.sbin.mysqld.
#

user            = mysql
socket          = /var/run/mysqld/mysqld.sock
port            = 3306
basedir         = /usr
datadir         = /var/lib/mysql
tmpdir          = /tmp
skip-external-locking
#

我在 RegexBuddy for Perl 中尝试了 Regex,它匹配文本文件中的所有内容,但由于某种原因,它无法在命令行上使用 perl。

我将不胜感激。

提前致谢。

【问题讨论】:

我不确定预期的结果应该是什么。 @cornuz 我认为从问题中可以明显看出这一点。我说“不是替换文件中的所有文本,[以下]是我最终得到的......”所以这几乎解释了它。同样从正则表达式来看,我认为我需要什么结果似乎很清楚。 【参考方案1】:

您正在逐行读取文件,因此只有第一行与您的正则表达式匹配。你想要做的——如果你真的想删除大部分内容——是通过使用-0 选项来删除文件,例如-0777。这是行结束处理,777 只是一个按惯例用作八进制数的数字,大到足以导致文件乱码。

perl -0777 -i -pe 's/(\[mysqld\][^\^]+)/$1\nsometext/g' test.txt

另外,我替换了你的引号。如果你在 *nix 中,看起来你是,单引号是更可取的。例如,$1 不会被 shell 插值。

【讨论】:

非常感谢。我不得不将其修改为:perl -0777 -i -pe "s/(\[mysqld\][^\^]+)/\1\nsometext/g" test.txt,因为稍后我将需要使用一些 shell 变量。干杯。 干杯。你知道我如何使用单引号包含 shell 变量吗?!在双引号中我做了这样的事情:perl -0777 -i -pe "s/(\[mysqld\][^\^]+)/\1\n$var/g" test.txt,很高兴知道我如何使用单引号来实现同样的事情?!再次感谢。 @ChuckUgwuh 您可以在单行中插入 shell 变量,是的。但是如果你使用双引号,这意味着打算成为 perl 变量的变量也会被插值,所以会造成混淆。你为什么不试试看:perl -lwe "print $var" 谢谢。我会去做。看来双引号更适合我的具体情况,所以我现在会坚持下去,直到我弄清楚如何用单引号来做。干杯。 @ChuckUgwuh:通过命令行(一行)将变量传递到 Perl 的环境中:perlvar=$shellvar perl ... '...$ENV"$perlvar"...' 或者您可以将其导出(可能在单独的行上):export shellvar; perl ... '...$ENV"shellvar"...'。前者使其仅可用于单个子进程(可能还有其子进程)的环境,而后者使其可用于任何子进程。【参考方案2】:

-p switch 使 Perl 遍历输入的每一行 并为每一行执行给定的代码(然后打印这些行)。具体来说,命令

perl -p -e 'SOME_CODE_HERE;'

完全等同于运行以下 Perl 程序:

LINE: while (<>) 
    SOME_CODE_HERE;
 continue 
    print or die "-p destination: $!\n";

您的正则表达式似乎旨在一次匹配多行,如果 Perl 逐行处理输入,这显然不起作用。要使其按预期工作,您有(至少)两种选择:

    通过使用-0<i>NNN</i> switch 改变 Perl 对什么构成行的概念。特别是,开关 -0777 使 Perl 将每个输入文件视为单个“行”。

    将您的代码重写为例如使用.. flip-flop operator。

顺便说一句,我强烈怀疑您的正则表达式并不像您认为的那样。特别是,[^\^]+ 匹配一个或多个不包含插入符号的字符串 (^)。由于您的输入似乎不太可能包含任何插入符号,因此这似乎基本上等同于(?s:.+)(或者如果您使用/s modifier,则只是.+)。

【讨论】:

以上是关于Perl 命令行多行替换的主要内容,如果未能解决你的问题,请参考以下文章

sed多行模板替换

vi删除多行,替换,复制

从另一个 Perl 脚本内部调用 Perl 搜索和替换命令时不起作用

如何从命令行快速替换文件中的字符串

perl在IC领域应用—复制多行通配符替换为数字

putty里删除替换多行(整段)