简体   繁体   English

如何使用 Perl 中的 sed?

[英]How do you use sed from Perl?

I know how to use sed with grep , but within Perl the below fails.我知道如何将sedgrep一起使用,但在 Perl 内,以下失败。 How can one get sed to work within a Perl program?如何让sed在 Perl 程序中工作?

chomp (my @lineNumbers=`grep -n "textToFind" $fileToProcess | sed -n 's/^\([0-9]*\)[:].*/\1/p'`)

Suggestion: Use Perl regular expressions and replacements instead of grep or sed.建议:使用 Perl 正则表达式和替换,而不是 grep 或 sed。

It's approximentally the same syntax, but more powerful.它的语法大致相同,但功能更强大。 Also in the end it will be more efficient than invoking the extra sed process.最后,它也会比调用额外的 sed 进程更有效。

Anything you need to do with grep or sed can be done natively in perl more easily.您需要对 grep 或 sed 做的任何事情都可以在 perl 中更轻松地完成。 For instance (this is roughly right, but probably wrong):例如(这大致正确,但可能是错误的):

my @linenumbers;
open FH "<$fileToProcess";
while (<FH>)
{
   next if (!m/textToFind/);
   chomp;
   s/^\([0-9]*\)[:].*/\1/;
   push @lineNumbers, $_;
}

I'm surprised that nobody mentioned the s2p utility, which translates sed "scripts" (you know, most of the time oneliners) to valid perl.我很惊讶没有人提到s2p实用程序,它将 sed“脚本”(你知道,大多数时候是 oneliners)转换为有效的 perl。 (And there's an a2p utility for awk too...) (还有一个用于 awk 的 a2p 实用程序......)

Supposedly Larry Wall wrote Perl because he found something that was impossible to do with sed and awk.据说 Larry Wall 写了 Perl,因为他发现了 sed 和 awk 不可能做的事情。 The other answers have this right, use Perl regular expressions instead.其他答案有这个权利,请改用 Perl 正则表达式。 Your code will have fewer external dependencies, be understandable to more people (Perl's user base is much bigger than sed user base), and your code will be cross-platform with no extra work.您的代码将具有更少的外部依赖,更多人可以理解(Perl 的用户群比 sed 用户群大得多),并且您的代码将是跨平台的,无需额外工作。

Edit: Paul Tomblin relates an excellent story in his comment on my answer.编辑:Paul Tomblin 在他对我的回答的评论中讲述了一个精彩的故事。 I'm putting it here to increase it's prominence.我把它放在这里是为了增加它的知名度。

"Henry Spencer, who did some amazing things with Awk, claimed that after demoing some awk stuff to Larry Wall, Larry said he wouldn't have bothered with Perl if he'd known." “Henry Spencer 用 Awk 做了一些了不起的事情,他声称在向 Larry Wall 演示了一些 awk 的东西后,Larry 说如果他知道他就不会打扰 Z0114AD06D728F1834E36FE14。” – Paul Tomblin ——保罗·汤布林

Use power Luke:使用权力卢克:

$ echo -e "a\nb\na"|perl -lne'/a/&&print$.'
1
3

Thus when you want same think as this slow and overcomplicated grep and sed combination you can do it far simpler and faster in perl itself:因此,当您想要与这种缓慢且过于复杂grepsed组合相同的想法时,您可以在 perl 本身中更简单更快地完成它:

my @linenumbers;
open my $fh, '<', $fileToProcess or die "Can't open $fileToProcess: $!";
while (<$fh>)
{
   /textToFind/ and push @lineNumbers, $.;
}
close $fh;

Or with same memory culprits as the original solution或与原始解决方案相同的 memory 罪魁祸首

my @linenumbers = do {
    open my $fh, '<', $fileToProcess or die "Can't open $fileToProcess: $!";
    my $i;
    map { ( ++$i ) x /textToFind/ } <$fh>
};

You can use您可以使用

perl -pe 's/search/replace/g'

in place of代替

sed 's/search/replace/'

.. However.. .. 然而..

Those are meant for command line or shell scripts.这些适用于命令行或 shell 脚本。 Since youre already in a perl script, the correct answer was given by "Paul Tomblin" above.由于您已经在 perl 脚本中,因此上面的“Paul Tomblin”给出了正确答案。

Have fun, eKerner.com玩得开心,eKerner.com

If you had a large sed expression, you could use s2p , to convert it into a perl program.如果您有一个大的sed表达式,您可以使用s2p将其转换为perl程序。

If you run < s2p 's/^\([0-9]*\)[:].*/\1/p' >, this is what you would get:如果你运行 < s2p 's/^\([0-9]*\)[:].*/\1/p' >,你会得到:

#!/opt/perl/bin/perl -w
eval 'exec /opt/perl/bin/perl -S $0 ${1+"$@"}'
  if 0;
$0 =~ s/^.*?(\w+)[\.\w+]*$/$1/;

use strict;
use Symbol;
use vars qw{ $isEOF $Hold %wFiles @Q $CondReg
         $doAutoPrint $doOpenWrite $doPrint };
$doAutoPrint = 1;
$doOpenWrite = 1;
# prototypes
sub openARGV();
sub getsARGV(;\$);
sub eofARGV();
sub printQ();

# Run: the sed loop reading input and applying the script
#
sub Run(){
    my( $h, $icnt, $s, $n );
    # hack (not unbreakable :-/) to avoid // matching an empty string
    my $z = "\000"; $z =~ /$z/;
    # Initialize.
    openARGV();
    $Hold    = '';
    $CondReg = 0;
    $doPrint = $doAutoPrint;
CYCLE:
    while( getsARGV() ){
    chomp();
    $CondReg = 0;   # cleared on t
BOS:;
# s/^\([0-9]*\)[:].*/\1/p
{ $s = s /^(\d*)[:].*/${1}/s;
  $CondReg ||= $s;
  print $_, "\n" if $s;
}
EOS:    if( $doPrint ){
            print $_, "\n";
        } else {
        $doPrint = $doAutoPrint;
    }
        printQ() if @Q;
    }

    exit( 0 );
}
Run();

# openARGV: open 1st input file
#
sub openARGV(){
    unshift( @ARGV, '-' ) unless @ARGV;
    my $file = shift( @ARGV );
    open( ARG, "<$file" )
    || die( "$0: can't open $file for reading ($!)\n" );
    $isEOF = 0;
}

# getsARGV: Read another input line into argument (default: $_).
#           Move on to next input file, and reset EOF flag $isEOF.
sub getsARGV(;\$){
    my $argref = @_ ? shift() : \$_; 
    while( $isEOF || ! defined( $$argref = <ARG> ) ){
    close( ARG );
    return 0 unless @ARGV;
    my $file = shift( @ARGV );
    open( ARG, "<$file" )
    || die( "$0: can't open $file for reading ($!)\n" );
    $isEOF = 0;
    }
    1;
}

# eofARGV: end-of-file test
#
sub eofARGV(){
    return @ARGV == 0 && ( $isEOF = eof( ARG ) );
}

# makeHandle: Generates another file handle for some file (given by its path)
#             to be written due to a w command or an s command's w flag.
sub makeHandle($){
    my( $path ) = @_;
    my $handle;
    if( ! exists( $wFiles{$path} ) || $wFiles{$path} eq '' ){
        $handle = $wFiles{$path} = gensym();
    if( $doOpenWrite ){
        if( ! open( $handle, ">$path" ) ){
        die( "$0: can't open $path for writing: ($!)\n" );
        }
    }
    } else {
        $handle = $wFiles{$path};
    }
    return $handle;
}

# printQ: Print queued output which is either a string or a reference
#         to a pathname.
sub printQ(){
    for my $q ( @Q ){
    if( ref( $q ) ){
            # flush open w files so that reading this file gets it all
        if( exists( $wFiles{$$q} ) && $wFiles{$$q} ne '' ){
        open( $wFiles{$$q}, ">>$$q" );
        }
            # copy file to stdout: slow, but safe
        if( open( RF, "<$$q" ) ){
        while( defined( my $line = <RF> ) ){
            print $line;
        }
        close( RF );
        }
    } else {
        print $q;
    }
    }
    undef( @Q );
}

Not exactly worth doing on small expressions.不完全值得在小表情上做。

Here's how you can use Perl as a replacement for Sed:以下是如何使用 Perl 替代 Sed 的方法:

Instead of:代替:

sed "s/xxx/yyy/g" files_to_process

Use:利用:

perl -i.bak -pe "s/xxx/yyy/g" files_to_process

This will modify the files in-place and make a backup ( .bak ) of each modified file.这将就地修改文件并对每个修改的文件进行备份 ( .bak )。

It is easier to use Perl than to use grep and sed;使用 Perl 比使用 grep 和 sed 更容易; see another answer .看到另一个答案

Your code failed because Perl messed with the backslashes in your sed code.您的代码失败,因为 Perl 与 sed 代码中的反斜杠混淆。 To prevent this, write your sed code in 'a single-quoted Perl string' , then use \Q$sedCode\E to interpolate the code into the shell command.为防止这种情况,请将您的 sed 代码写入'a single-quoted Perl string' ,然后使用\Q$sedCode\E将代码插入 Z2591C98B70119FE624898B1E424 命令中。 (About \Q...E , see perldoc -f quotemeta . Its usual purpose is to quote characters for regular expressions, but it also works with shell commands .) (关于\Q...E ,请参阅perldoc -f quotemeta 。它通常的目的是为正则表达式引用字符,但它也适用于 shell 命令。)

my $fileToProcess = "example.txt";
my $sedCode = 's/^\([0-9]*\)[:].*/\1/p';
chomp(my @linenumbers =
      `grep -n "textToFind" \Q$fileToProcess\E | sed -n \Q$sedCode\E`);
printf "%s\n", join(', ', @linenumbers);

Given example.txt with给定example.txt

this has textToFind
this doesn't
textToFind again
textNotToFind

the output is 1, 3 . output 是1, 3

Edited: OK, I fixed it now.编辑:好的,我现在修好了。

use File::Grep qw/fmap/;

my @lineNumbers = fmap { /$pattern/ ? $_[1] : () } $fileToProcess;

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

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