tags:

views:

2611

answers:

7

Hi, I know how to use sed with grep, but within Perl the below fails. How can one get sed to work within a Perl program?

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

Suggestion: Use Perl regular expressions and replacements instead of grep or 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.

Jason Cohen
grep is also unnecessary
dsm
thanks @dsm! I updated the answer
Jason Cohen
Agreed, better not to take unnecessary detours outside the language's available API's.
Dana the Sane
Plus it's more portable.
slim
Personal taste, perhaps, but some example code would be spiffy.
Chris Lutz
@Chris: You're right, so I upvoted Paul Tomblin who already did so.
Jason Cohen
+7  A: 

Anything you need to do with grep or sed can be done natively in perl more easily. 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, $_;
}
Paul Tomblin
you need a final / on your if, but otherwise looks good.
Mez
Thanks, Martin. It's fixed now.
Paul Tomblin
A matter of taste, but: I would use 'next unless' rather than !m///
slim
"unless" just seems too much like INTERCAL's "COME FROM" for me.
Paul Tomblin
I've seen it recommended that you use $1 instead of \1 in Perl. It's supposed to have better performance or something like that.
Nathan Fellman
A: 

Edited: OK, I fixed it now.

use File::Grep qw/fmap/;

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

Supposedly Larry Wall wrote Perl because he found something that was impossible to do with sed and awk. The other answers have this right, use Perl regular expressions instead. Your code will have fewer external dependencies, be understandable to mode people (Perl's user base is much bigger than sed user base), and your code will be cross-platform with no extra work.

Edit: Paul Tombin relates an excellent story in his comment on my answer. 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." – Paul Tomblin

James Thompson
Look for 's2p', the sed to Perl converter; comes with Perl still.
Jonathan Leffler
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.
Paul Tomblin
I am extremely glad that Larry *hadn't* known. :)
Marcus
+1  A: 

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:

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

If you had a large sed expression, you could use s2p, to convert it into a perl program.

If you run  <s2p 's/^\([0-9]*\)[:].*/\1/p'>, this is what you would get:

#!/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.

Brad Gilbert
Looks like s2p is a good place for someone who wants to do some optimization and improvement of existing open source software.
Chris Lutz
+2  A: 

I'm surprised that nobody mentioned the s2p utility, which translates sed "scripts" (you know, most of the time oneliners) to valid perl. (And there's an a2p utility for awk too...)

Zsolt Botykai