相关文章推荐
阳刚的蚂蚁  ·  Linux ...·  2 周前    · 
爱玩的登山鞋  ·  oracle ...·  7 月前    · 
大鼻子的山羊  ·  fastapi token 验证-掘金·  1 年前    · 
没读研的猴子  ·  用ffmpeg从python ...·  1 年前    · 

sed命令在命令行中工作,但在perl脚本中不工作

0 人关注

我有一个文件,我必须替换所有像$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;

我错过了什么? 我认为添加一些反斜线会有帮助。我试着添加一些,但没有成功。

linux
perl
sed
confused
confused
发布于 2017-10-13
1 个回答
zdim
zdim
发布于 2017-10-13
已采纳
0 人赞同

在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以上版本中,你可以使用non-destructive /r modifier

print $fh_out s/\$([^{][a-z_]*)/\${$1}/gir;

替换后的字符串被返回(原件不变),右为print

输出文件,最后移到原始文件上,应使用File::Temp.以这种方式覆盖原始文件会改变$file的节点号;如果担心这个问题,请参见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 } );