我有一个文件,其中我必须替换所有单词,如$ xyz,对于他们我必须替换像这样:
$xyz with ${xyz}.
$abc_xbs with ${abc_xbc}
$ab,$cd with ${ab},${cd}
这个文件也有一些像$ {abcd}这样的词,我不需要改变。 我正在使用此命令
sed -i' s?\ $([A-Z _] +)?\ $ {\ 1}?g'文件
它在命令行上工作正常但不在perl脚本中
sed -i 's?\$\([A-Z_]\+\)?\$\{\1\}?g' file
;
我缺少什么? 我认为添加一些反斜杠会有所帮助。我尝试添加一些但没有成功。
由于
答案 0 :(得分:1)
在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
以保留备份。
一个合理的问题&#34; 不是更短的方式&#34;来了
这是一个,使用方便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
是一个很好的模块,这是合法的,虽然它确实缩短了很多东西。