2009-03-04 9 views
8

Sé cómo usar sed con grep, pero dentro de Perl falla lo siguiente. ¿Cómo se puede obtener sed para trabajar dentro de un programa Perl?¿Cómo se usa sed de Perl?

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

Respuesta

8

Me sorprende que nadie haya mencionado la utilidad s2p, que traduce los "scripts" de sed (ya sabes, la mayoría de las veces, "oneliners") a perl válidos. (Y hay una utilidad a2p para awk también ...)

25

Sugerencia: utilice expresiones regulares de Perl y reemplazos en lugar de grep o sed.

Tiene aproximadamente la misma sintaxis, pero es más potente. Además, al final será más eficiente que invocar el proceso extra sed.

+0

grep también es innecesario – dsm

+0

gracias @dsm! Actualicé la respuesta –

+0

de acuerdo, es mejor no tomar desvíos innecesarios fuera de las API disponibles del idioma. –

12

Todo lo que necesite hacer con grep o sed se puede hacer de forma nativa en Perl más fácilmente. Por ejemplo (esto es más o menos bien, pero probablemente mal):

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

necesita un final/en su if, pero por lo demás se ve bien. – Mez

+0

Gracias, Martin. Está arreglado ahora. –

+1

Una cuestión de gusto, pero: usaría 'siguiente a menos que' en lugar de! M /// – slim

0

Editado: OK, me fijo ahora.

use File::Grep qw/fmap/; 

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

Supuestamente Larry Wall escribió Perl porque encontró algo que era imposible de hacer con sed y awk. Las otras respuestas tienen este derecho, en su lugar usa expresiones regulares de Perl. Su código tendrá menos dependencias externas, será comprensible para más personas (la base de usuarios de Perl es mucho mayor que la base de usuarios sed) y su código será multiplataforma sin trabajo adicional.

Editar: Paul Tomblin relata una excelente historia en su comentario sobre mi respuesta. Lo estoy poniendo aquí para aumentar su importancia.

"Henry Spencer, que hizo algunas cosas asombrosas con Awk, afirmó que después de hacerle algunas demostraciones a Larry Wall, Larry dijo que no se habría molestado con Perl si lo hubiera sabido". - Paul Tomblin

+0

Busque 's2p', el convertidor sed a Perl; viene con Perl todavía. –

+1

Henry Spencer, que hizo algunas cosas asombrosas con Awk, afirmó que después de hacerle algunas demostraciones a Larry Wall, Larry dijo que no se habría molestado con Perl si lo hubiera sabido. –

+4

Estoy muy contento de que Larry * no lo haya sabido. :) – Marcus

2

uso de energía Lucas:

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

De este modo cuando desee misma piensan que este lento y excesivamente complicada grep y sed combinación que puede hacer que sea mucho más sencillo y más rápido en Perl en sí:

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

O con los mismos culpables de memoria como la solución original

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

¿Saltó los espacios en "perl -lne"/a/&& print $. '"A propósito? Estuve tentado de editarlo para agregar espacios pero no sabía si había alguna razón para ellos. –

+0

No, no hay ningún propósito, pero solo para ser más corto como de costumbre cuando se codifica el golf. En Perl 5.10 puede ser incluso más breve perl -nE '/ a/&& say $.' Debería funcionar exactamente igual que perl -ne '/ a/e imprimir "$. \ N"' o perl -ne 'if ($ _ = ~ m/a /) {print $., "\ N"}' etc. . –

1

Si tenía una expresión grande sed, puede usar s2p, para convertirla en un programa perl.

Si ejecuta   < s2p 's/^\([0-9]*\)[:].*/\1/p'>, esto es lo que se obtendría:

#!/opt/perl/bin/perl -w 
eval 'exec /opt/perl/bin/perl -S $0 ${1+"[email protected]"}' 
    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); 
} 

No es exactamente la pena hacerlo en pequeñas expresiones.

+0

Parece que s2p es un buen lugar para alguien que quiere hacer algo de optimización y mejora del software de código abierto existente. –

1

Puede utilizar

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

en lugar de

sed 's/search/replace/' 

.. .. Sin embargo

Aquellos están destinados para la línea de comandos o shell scripts. Dado que ya está en una secuencia de comandos de Perl, , la respuesta correcta fue dada por "Paul Tomblin" más arriba.

divertirse, eKerner.com

+0

Gracias, esto es exactamente lo que estaba buscando. En mi caso (un script bash), necesitaba algo como 'sed', pero con el soporte adecuado de PCRE, y nada es compatible con PCRE mejor que el propio Perl. – selurvedu

0

Así es como se puede utilizar Perl como un reemplazo para Sed:

En lugar de:

sed "s/xxx/yyy/g" files_to_process 

Uso:

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

Esto modificará los archivos in-place y haga una copia de seguridad (.bak) de cada archivo modificado.

0

Es más fácil usar Perl que usar grep y sed; ver another answer.

Su código falló porque Perl se equivocó con las barras diagonales inversas en su código sed. Para evitar esto, escriba su código sed en 'a single-quoted Perl string', luego use \Q$sedCode\E para interpolar el código en el comando de shell. (Sobre \Q...E, ver perldoc -f quotemeta. Su propósito es habitual citar caracteres de expresiones regulares, pero it also works with shell commands.)

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); 

Dado example.txt con

this has textToFind 
this doesn't 
textToFind again 
textNotToFind 

la salida es 1, 3.