sed命令可在命令行上运行,但不能在perl脚本中使用 [英] sed command working on command line but not in perl script

查看:210
本文介绍了sed命令可在命令行上运行,但不能在perl脚本中使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个文件,其中我必须替换所有单词,例如$ xyz,对于它们,我必须替换以下内容:

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 with ${xyz}.
$abc_xbs with ${abc_xbc}
$ab,$cd  with ${ab},${cd}

此文件也有一些词,例如$ {abcd},我不必更改. 我正在使用此命令

This file also have some words like ${abcd} which i don't have to change. I am using this command

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

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

它可以在命令行上正常工作,但不能在perl脚本中像

its working fine on command line but not inside a perl script as

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.

谢谢

推荐答案

在Perl脚本中,您需要有效的Perl语言,就像在C程序中需要有效的C文本一样.在终端中,sed..被外壳程序理解并作为命令运行,但是在Perl程序中,它只是一堆单词,而sed..行不是有效的Perl.

In a Perl script you need valid Perl language, just like you need valid C text in a C program. 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.

您可能需要在qx()(反引号)或system()中使用它,以便将其作为外部命令运行.然后,您确实需要"一些反斜杠",这会使事情变得有些挑剔.

You would need this inside qx() (backticks) or system() so that it is run as an external command. Then you'd indeed need "some backslashes," which is where things get a bit picky.

但是为什么要从Perl脚本运行sed命令?用Perl完成工作

But why run a sed command from a Perl script? Do the job with 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).

在5.14及更高版本中,您可以使用非破坏性 /r 修饰符

With 5.14+ you can use the non-destructive /r modifier

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

,返回更改后的字符串(且原始字符串不变),对于print.

with which the changed string is returned (and original is unchanged), right for the print.

最后应使用文件:来制作输出文件,最后移到原始文件上:温度.用这种方法覆盖原始文件会更改$file的inode编号;如果担心的话,请参阅这篇文章,以了解如何更新原始inode.

The output file, in the end moved over the original, should be made using File::Temp. Overwriting the original this way changes $file's inode number; if that's a concern see this post for example, for how to update the original inode.

单行(命令行)版本,易于测试

A one-liner (command-line) version, to readily test

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

这仅打印到控制台.要更改原始内容,请添加-i(就地),或添加-i.bak保持备份.

This only prints to console. To change the original add -i (in-place), or -i.bak to keep backup.

提出了一个合理的问题"没有更短的方法".

A reasonable question of "Isn't there a shorter way" came up.

这里是一个,使用方便的路径:: Tiny 用于不大的文件,因此我们可以将其读取为字符串.

Here is one, using the handy Path::Tiny for a file that isn't huge so we can read it into a string.

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.

但是,由于模块的版本为0.077,您可以很好地完成

However, since module's version 0.077 you can nicely do

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.

所以这最终将它切成一条漂亮的线.

So this cuts it to one nice line after all.

我想补充一点,削掉代码行几乎是不值得的,尽管如果这样做会干扰对代码结构和正确性的关注,那肯定会导致麻烦.但是,Path::Tiny是一个很好的模块,这是合法的,尽管它确实使事情大大缩短了.

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.

这篇关于sed命令可在命令行上运行,但不能在perl脚本中使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆