Как вы используете sed от Perl?

Я знаю, как использовать sed С grep, но в Perl ниже не удается. Как можно получить sed для работы в программе Perl?

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

10 ответов


Я удивлен, что никто не упомянул s2p утилита, которая переводит sed "скрипты" (вы знаете, большую часть времени oneliners) в действительный perl. (И есть утилита a2p для awk тоже...)


предложение: используйте регулярные выражения и замены Perl вместо grep или sed.

Это примерно тот же синтаксис, но более мощный. Также в конце концов это будет более эффективно, чем вызов дополнительного процесса sed.


все, что вам нужно сделать с grep или sed, можно сделать изначально в perl более легко. Например (это примерно правильно, но, вероятно, неправильно):

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

предположительно Ларри Уолл написал Perl, потому что он нашел что-то, что было невозможно сделать с sed и awk. Другие ответы имеют это право, вместо этого используйте регулярные выражения Perl. Ваш код будет иметь меньше внешних зависимостей, будет понятен большему количеству людей (база пользователей Perl намного больше, чем база пользователей sed), и ваш код будет кросс-платформенным без дополнительной работы.

Edit: пол Томблин рассказывает отличную историю в своем комментарии к моему ответу. Я кладу его сюда увеличить это значение.


используйте power Luke:

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

таким образом, когда вы хотите же думать как это медленно и запутанно grep и sed комбинация вы можете сделать это намного проще и быстрее в самом perl:

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

или с теми же виновниками памяти, что и исходное решение

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

если бы у вас был большой sed выражение, Вы можете использовать s2p, чтобы преобразовать его в .

если вы запустите s2p 's/^\([0-9]*\)[:].*//p'>, это то, что вы получите:

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

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 = "0"; $z =~ /$z/;
    # Initialize.
    openARGV();
    $Hold    = '';
    $CondReg = 0;
    $doPrint = $doAutoPrint;
CYCLE:
    while( getsARGV() ){
    chomp();
    $CondReg = 0;   # cleared on t
BOS:;
# s/^\([0-9]*\)[:].*//p
{ $s = s /^(\d*)[:].*//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( ": 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( ": 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( ": 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 );
}

не совсем стоит делать на небольших выражениях.


можно использовать

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

на месте

sed 's/search/replace/'

.. Однако..

Они предназначены для сценариев командной строки или оболочки. Поскольку вы уже в скрипте perl, правильный ответ был дан "Павел Томблин" выше.

удачи, eKerner.com


Edited: хорошо, я исправил это сейчас.

use File::Grep qw/fmap/;

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

вот как вы можете использовать Perl в качестве замены для Sed:

вместо:

sed "s/xxx/yyy/g" files_to_process

использование:

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

это изменит файлы на месте и сделайте резервную копию (.bak) каждого измененного файла.


легче использовать Perl, чем использовать grep и sed; см. еще один ответ.

ваш код не удался, потому что Perl испортил обратные косые черты в вашем коде sed. Чтобы предотвратить это, напишите код sed в 'a single-quoted Perl string', а затем использовать \Q$sedCode\E для интерполяции кода в команду оболочки. (О \Q...E см. perldoc -F в quotemeta. Его обычная цель-цитировать символы для регулярных выражений, но он также работает с Shell команды.)

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

дано example.txt С

this has textToFind
this doesn't
textToFind again
textNotToFind

выход 1, 3.