在 Perl 脚本中,您需要有效的 Perl 语言,就像您在 C 程序中需要有效的 C 文本一样。在终端中 sed.. 被 shell 理解并作为命令运行,但在 Perl 程序中它只是一堆单词,并且该行 sed.. 不是有效的 Perl。
您需要在 qx()(反引号)或 system() 中使用它,以便它作为外部命令运行。那么你确实需要“一些反斜杠”,这就是事情变得有点挑剔的地方。
但是为什么要从 Perl 脚本运行 sed 命令呢?使用 Perl 完成工作
use warnings;
use strict;
use File::Copy 'move';
my $file = 'filename';
my $out_file = 'new_' . $file;
open my $fh, '<', $file or die "Can't open $file: $!";
open my $fh_out, '>', $out_file or die "Can't open $out_file: $!";
while (<$fh>)
{
s/\$( [^{] [a-z_]* )/\${$1}/gix;
print $fh_out $_;
}
close $fh_out;
close $fh;
move $out_file, $file or die "Can't move $out_file to $file: $!";
正则表达式使用一个否定字符类,[^...],来匹配{ 之外的任何字符,而不是$ 之后的字符,因此排除了已经大括号的单词。然后它匹配一系列字母或下划线,就像问题中一样(可能没有,因为第一个非{ 已经提供了至少一个)。
5.14+ 你可以使用非破坏性 /r modifier
print $fh_out s/\$([^{][a-z_]*)/\${$1}/gir;
返回更改后的字符串(原始字符串未更改),适合print。
最终移动到原始文件上的输出文件应使用File::Temp。以这种方式覆盖原始会更改$file 的inode 号;如果这是一个问题,请参阅 this post 例如,了解如何更新原始 inode。
单行(命令行)版本,易于测试
perl -wpe's/\$([^{][a-z_]*)/\${$1}/gi' file
这只会打印到控制台。要更改原始添加 -i(就地),或 -i.bak 以保留备份。
出现了一个合理的问题“难道没有更短的方法”。
这是一个,使用方便的Path::Tiny 处理一个不大的文件,以便我们可以将其读入字符串。
use warnings;
use strict;
use Path::Tiny;
my $file = 'filename';
my $out_file = 'new_' . $file;
my $new_content = path($file)->slurp =~ s/\$([^{][a-z_]*)/\${$1}/gir;
path($file)->spew( $new_content );
第一行将文件读入一个字符串,替换运行在该字符串上;更改的文本被返回并分配给一个变量。然后将带有新文本的变量写在原始变量上。
通过将第一行的表达式而不是第二行的变量放入,可以将两行压缩为一个。但是在一个(复杂的)语句中打开同一个文件两次并不是完全可靠的做法,我不推荐这样的代码。
但是,自从模块的版本 0.077 以来,您可以很好地做到这一点
path($file)->edit_lines( sub { s/\$([^{][a-z_]*)/\${$1}/gi } );
或使用edit 将文件转换为字符串并对其应用回调。
所以这毕竟把它剪成了一条漂亮的线。
我想补充一点,削减代码行通常不值得付出努力,但如果它稍微扰乱了对代码结构和正确性的关注,它肯定会导致麻烦。但是,Path::Tiny 是一个很好的模块,它是合法的,但它确实缩短了很多时间。