Perl命令行多行替换

25

我正在尝试使用命令行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工具中为Perl尝试了正则表达式,它匹配了文本文件中的所有内容,但出于某种原因,使用命令行中的Perl时它不起作用。

我需要一些帮助。

提前感谢您的帮助。


5
我不确定预期结果应该是什么。 - cornuz
1
@cornuz 我认为这从问题中就很明显了。我说“而不是替换文件中的所有文本,[以下]是我最终得到的...”所以这基本上已经解释清楚了。此外,从正则表达式来看,我认为结果很明显。 - ObiHill
2个回答

46

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

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变量。干杯。 - ObiHill
你好。您知道我如何在单引号中包含shell变量吗?!在双引号中,我会像这样做:perl -0777 -i -pe "s/(\[mysqld\][^\^]+)/\1\n$var/g" test.txt,很高兴知道我如何使用单引号实现相同的效果?!再次感谢。 - ObiHill
@ChuckUgwuh,你可以在一行命令中插入shell变量。但是如果你使用双引号,那么本来应该作为perl变量的变量也会被插值,这样会很混乱。为什么不试一下呢:perl -lwe "print $var" - TLP
谢谢。我会这样做的。看起来双引号在我的特定情况下效果更好,所以我现在会坚持使用双引号,直到我弄清如何使用单引号为止。干杯。 - ObiHill
@ChuckUgwuh:将变量通过命令行传递到Perl的环境中(一行代码):perlvar=$shellvar perl ... '...$ENV{"$perlvar"}...' 或者您可以导出它(可能在不同的行上):export shellvar; perl ... '...$ENV{"shellvar"}...'。前者仅使其在单个子进程(及其可能的子进程)的环境中可用,而后者使其对任何子进程都可用。 - Dennis Williamson

7

-p开关会导致Perl遍历输入的每一行,并对它们执行给定的代码(然后打印这些行)。具体来说,命令:

perl -p -e 'SOME_CODE_HERE;'

这与运行以下Perl程序完全等效:

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

您的正则表达式似乎意图一次匹配多行,但如果Perl逐行处理输入,则显然无法工作。为使其按预期工作,您至少有以下两个选项:

  1. 使用-0NNN开关更改Perl对什么构成一行的概念。特别地,开关-0777使Perl将每个输入文件视为单个“行”。

  2. 重写您的代码,例如使用..翻转操作符

顺便说一下,我强烈怀疑您的正则表达式不是您想要的意思。特别地,[^\^]+匹配一个不包含脱字符(^)的一个或多个字符的字符串。由于您的输入似乎不可能包含任何脱字符,因此这似乎与(?s:.+)(如果使用了/s修饰符,则只需使用.+)基本等同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接