Pergunta

Eu sei como usar sed com grep, mas dentro de Perl a seguir falhar. Como pode-se obter sed ao trabalho dentro de um programa Perl?

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

Solução

Estou surpreso que ninguém mencionou o href="http://perldoc.perl.org/s2p.html" rel="nofollow noreferrer"> utilidade , o que se traduz "scripts" sed (você sabe, a maioria dos Oneliners tempo) para perl válido. (E há um utilitário a2p para awk também ...)

Outras dicas

Sugestão:. Use Perl expressões e substituições regulares em vez de grep ou sed

É approximentally a mesma sintaxe, mas mais poderoso. Também no final, será mais eficiente do que invocando o processo sed extra.

Qualquer coisa que você precisa fazer com grep ou sed pode ser feito de forma nativa em perl mais facilmente. Por exemplo (este é mais ou menos certo, mas provavelmente errado):

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

Supostamente Larry Wall escreveu Perl porque ele encontrou algo que era impossível fazer com sed e awk. As outras respostas têm esse direito, use expressões regulares Perl em seu lugar. Seu código terá menos dependências externas, ser compreensível para mais pessoas (base de usuários do Perl é muito maior do que a base de usuários sed), e seu código será multi-plataforma com nenhum trabalho extra.

Edit: Paul Tomblin relaciona uma história excelente em seu comentário sobre a minha resposta. Estou colocando isso aqui para aumentar a sua proeminência.

"Henry Spencer, que fez coisas incríveis com Awk, afirmou que depois demos algumas coisas awk para Larry Wall, Larry disse que não teria incomodado com Perl se soubesse." - Paul Tomblin

Use o poder Luke:

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

Assim, quando você quer mesmo pensar como esta combinação grep e sed lenta e complicada que você pode fazê-lo muito mais simples e mais rápido em perl-se:

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

Ou com os mesmos culpados de memória como a solução original

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

Se você tinha uma grande expressão sed, você poderia usar s2p, para convertê-lo em um programa de perl.

Se você executar <s2p 's/^\([0-9]*\)[:].*/\1/p'>, este é o que você obtém:

#!/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 );
}

Não exatamente vale a pena fazer em pequenas expressões.

Você pode usar

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

no lugar de

sed 's/search/replace/'

.. No entanto ..

Aqueles são voltados para linha de comando ou scripts shell. Desde você já está em um script perl, a resposta correta foi dada por "Paul Tomblin" acima.

Divirta-se, eKerner.com

Editado:. OK, eu fixa-lo agora

use File::Grep qw/fmap/;

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

Veja como você pode usar Perl como um substituto para Sed:

Em vez de:

sed "s/xxx/yyy/g" files_to_process

Use:

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

Isto irá modificar os arquivos in-place e fazer um backup (.bak) de cada arquivo modificado.

É mais fácil de usar Perl do que para uso grep e sed; consulte outra resposta .

O seu código falhou porque Perl mexeu com as barras invertidas em seu código sed. Para evitar isso, escrever o seu código de sed em 'a single-quoted Perl string', em seguida, usar \Q$sedCode\E para interpolar o código no comando shell. (Sobre \Q...E, consulte perldoc -f quotemeta . Sua finalidade usual é citar personagens para expressões regulares, mas ele também funciona com comandos 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);

example.txt Dado com

this has textToFind
this doesn't
textToFind again
textNotToFind

a saída é 1, 3.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top