Как вы используете sed из Perl? - PullRequest
8 голосов
/ 04 марта 2009

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

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

Ответы [ 10 ]

25 голосов
/ 04 марта 2009

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

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

14 голосов
/ 04 марта 2009

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

my @linenumbers;
open FH "<$fileToProcess";
while (<FH>)
{
   next if (!m/textToFind/);
   chomp;
   s/^\([0-9]*\)[:].*/\1/;
   push @lineNumbers, $_;
}
8 голосов
/ 05 марта 2009

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

4 голосов
/ 04 марта 2009

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

Редактировать: Пол Томблин рассказывает отличную историю в своем комментарии к моему ответу. Я помещаю это здесь, чтобы увеличить это выдающееся положение.

"Генри Спенсер, который сделал несколько удивительных вещей с Awk, заявил, что после демонстрации некоторого материала по awk Ларри Уоллу Ларри сказал, что не стал бы беспокоиться о Perl, если бы знал" - Пол Томблин

2 голосов
/ 04 марта 2009

Используйте силу Люка:

$ 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>
};
1 голос
/ 26 марта 2013

Вы можете использовать

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

вместо

sed 's/search/replace/'

.. Однако ..

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

Веселись, eKerner.com

1 голос
/ 05 марта 2009

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

Если вы запустите <<code>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 );
}

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

0 голосов
/ 10 ноября 2017

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

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

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 с

this has textToFind
this doesn't
textToFind again
textNotToFind

вывод 1, 3.

0 голосов
/ 06 апреля 2016

Вот как вы можете использовать Perl вместо Sed:

Вместо:

sed "s/xxx/yyy/g" files_to_process

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

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

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

0 голосов
/ 04 марта 2009

Отредактировано: ОК, я исправил это сейчас.

use File::Grep qw/fmap/;

my @lineNumbers = fmap { /$pattern/ ? $_[1] : () } $fileToProcess;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...