简体   繁体   English

sed命令在命令行上运行,但不在perl脚本中

[英]sed command working on command line but not in perl script

I have a file in which i have to replace all the words like $xyz and for them i have to substitutions like these: 我有一个文件,其中我必须替换所有单词,如$ xyz,对于它们,我必须替换为以下内容:

$xyz with ${xyz}.
$abc_xbs with ${abc_xbc}
$ab,$cd  with ${ab},${cd}

This file also have some words like ${abcd} which i don't have to change. 这个文件也有一些像$ {abcd}这样的词,我不必更改。 I am using this command 我正在使用此命令

sed -i 's?\\$([A-Z_]+)?\\${\\1}?g' file sed -i's?\\ $([A-Z _] +)?\\ $ {\\ 1}?g'文件

its working fine on command line but not inside a perl script as 它可以在命令行上正常工作,但不能在perl脚本中作为

sed -i 's?\\$\\([A-Z_]\\+\\)?\\$\\{\\1\\}?g' file ; sed -i 's?\\$\\([A-Z_]\\+\\)?\\$\\{\\1\\}?g' file

What i am missing? 我想念什么? I think adding some backslashes would help.I tried adding some but no success. 我认为添加一些反斜杠会有所帮助。我尝试添加一些但没有成功。

Thanks 谢谢

In a Perl script you need valid Perl language, just like you need valid C text in a C program. 在Perl脚本中,您需要有效的Perl语言,就像在C程序中需要有效的C文本一样。 In the terminal sed.. is understood and run by the shell as a command but in a Perl program it is just a bunch of words, and that line sed.. isn't valid Perl. 在终端中sed..被外壳理解并作为命令运行,但是在Perl程序中,它只是一堆单词,而该行sed..不是有效的Perl。

You would need this inside qx() (backticks) or system() so that it is run as an external command. 您可能需要在qx() (反引号)或system()中使用它,以便将其作为外部命令运行。 Then you'd indeed need " some backslashes ," which is where things get a bit picky. 然后,您确实需要“ 一些反斜杠 ”,这会使事情变得有些挑剔。

But why run a sed command from a Perl script? 但是,为什么要从Perl脚本运行sed命令呢? Do the job with Perl 用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: $!";

The regex uses a negated character class , [^...] , to match any character other than { following $ , thus excluding already braced words. 正则表达式使用否定的字符类 [^...]来匹配除$ {之外的任何其他字符,因此不包括已经大括号的单词。 Then it matches a sequence of letters or underscore, as in the question (possibly none, since the first non- { already provides at least one). 然后,它与一系列字母或下划线匹配,如在问题中一样(可能没有,因为第一个非{已经提供了至少一个)。

With 5.14+ you can use the non-destructive /r modifier 在5.14+中,您可以使用非破坏性的 /r 修饰符

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

with which the changed string is returned (and original is unchanged), right for the print . 用它返回更改后的字符串(原件不变),适合print

The output file, in the end moved over the original, should be made using File::Temp . 最后,应使用File :: Temp制作输出文件,并移至原始文件上方。 Overwriting the original this way changes $file 's inode number; 这样覆盖原始$file更改$file的inode编号; if that's a concern see this post for example, for how to update the original inode. 如果这是一个问题,请参阅此帖子 ,例如,有关如何更新原始inode的信息。

A one-liner (command-line) version, to readily test 单行(命令行)版本,易于测试

perl -wpe's/\$([^{][a-z_]*)/\${$1}/gi' file

This only prints to console. 这仅打印到控制台。 To change the original add -i (in-place), or -i.bak to keep backup. 要更改原始位置,请添加-i (就地)或-i.bak以保持备份。


A reasonable question of " Isn't there a shorter way " came up. 出现了一个合理的问题“ 没有更短的方法了 ”。

Here is one, using the handy Path::Tiny for a file that isn't huge so we can read it into a string. 这是一个使用方便的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 );

The first line reads the file into a string, on which the replacement runs; 第一行将文件读入一个字符串,在该字符串上进行替换; the changed text is returned and assigned to a variable. 更改后的文本将返回并分配给变量。 Then that variable with new text is written out over the original. 然后,带有新文本的变量将覆盖原始变量。

The two lines can be squeezed into one, by putting the expression from the first instead of the variable in the second. 通过将表达式从第一行而不是变量放在第二行,可以将这两行压缩为一个。 But opening the same file twice in one (complex) statement isn't exactly solid practice and I wouldn't recommend such code. 但是,在一个(复杂)语句中两次打开同一文件并不是完全可靠的做法,因此我不建议您使用这种代码。

However, since module's version 0.077 you can nicely do 但是,由于模块的版本为0.077,您可以很好地执行

path($file)->edit_lines( sub { s/\$([^{][a-z_]*)/\${$1}/gi } );

or use edit to slurp the file into a string and apply the callback to it. 或使用edit将文件插入字符串并对其应用回调。

So this cuts it to one nice line after all. 因此,这毕竟将其切成一条漂亮的线。

I'd like to add that shaving off lines of code mostly isn't worth the effort while it sure can lead to trouble if it disturbs the focus on the code structure and correctness even a bit. 我想补充一点,删节代码行几乎是不值得的,但是如果它干扰了对代码结构和正确性的关注,那肯定会导致麻烦。 However, Path::Tiny is a good module and this is legitimate, while it does shorten things quite a bit. 但是, Path::Tiny是一个很好的模块,这是合法的,尽管它确实使事情大大缩短了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM