【问题标题】:sed command working on command line but not in perl scriptsed 命令在命令行上工作,但不在 perl 脚本中
【发布时间】:2018-03-25 04:43:03
【问题描述】:

我有一个文件,我必须在其中替换 $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


    【解决方案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 以保留备份。


    出现了一个合理的问题“难道没有更短的方法”。

    这是一个,使用方便的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 是一个很好的模块,它是合法的,但它确实缩短了很多时间。

    【讨论】:

    • 这工作正常,但它没有更改文件,而是在 STDOUT 上打印输出,我是否必须将其写入临时文件,然后复制该文件。没有更短的方法吗?完成我的目标?
    • @confused 已编辑 -- 如果更多解释有帮助,请告诉我。
    • @confused 添加了一个更短的方法。
    • 嘿,我注意到了,还有一些像 $(xyz) 这样的字符串,我也不想替换它们。我必须对现在的表达做些什么改变?
    • 在 (
    猜你喜欢
    • 2015-11-28
    • 2021-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-08
    • 2021-12-14
    • 1970-01-01
    相关资源
    最近更新 更多