Подтвердить что ты не робот

Что каждый хакер Perl знает о perl -ne?

Я использую командную строку Perl с параметром -ne в течение многих лет, в основном для обработки текстовых файлов способами, недоступными sed. Пример:

cat in.txt | perl -ne "s/abc/def/; s/fgh/hij/; print;" > out.txt

Я понятия не имею, где я это узнал и только сегодня читаю perlrun и обнаружил, что существуют другие формы (perl -pe для пример).

Что еще мне знать о perl -ne?

4b9b3361

Ответ 1

perl -ne 'CODE' эквивалентен программе

while (<>) {
    CODE
}

perl -ane 'CODE' и perl -F/PATTERN/ -ane также являются хорошими идиомами, о которых нужно знать. Они эквивалентны

while (<>) {
    @F = split /\s+/, $_;
    CODE
}

и

while (<>) {
    @F = split /PATTERN/, $_;
    CODE
}

Пример: расширенный grep:

perl -ne 'print if/REGEX1/&&!/REGEX2/&&(/REGEX3/||/REGEX4/&&!/REGEX5/)' input

perl -F/,/ -ane 'print if $F[2]==4&&$F[3]ge"2009-07-01"&&$F[3]lt"2009-08-01"' file.csv


Особенно умный пример, который использует несогласованные фигурные скобки, здесь.

Ответ 2

Существует одна важная информация о скриптах perl -ne и perl -pe: они неявно используют <>.

"Почему это важно?" вы можете спросить.

Магический оператор <> использует открытую форму 2 arg. Если вы помните, 2 arg open включает спецификацию режима с именем файла в одном аргументе. Старый вызов стиля open FILE, $foo уязвим для манипулирования файловым режимом. Особенно интересным способом в этом контексте является | - вы открываете дескриптор канала для процесса, который вы выполняете.

Возможно, вы думаете: "Большое дело!", но это так.

  • Представьте себе задание cron, выполняемое root, чтобы выполнить файлы журналов в некоторых каталогах.
  • script вызывается как script *.
  • Представьте файл в этом каталоге с именем |rm -rf /.

Что происходит?

  • Оболочка расширяет *, и мы получаем script file_1 file_2 '|rm -rf /' file_4
  • script обрабатывает file_1 и file_2.
  • Затем он открывает дескриптор для STDIN rm -rf /.
  • Далее следует количество операций с диском.
  • file_4 больше не существует, поэтому мы не можем его открыть.

Конечно, возможности бесконечны.

Вы можете прочитать больше обсуждения этой проблемы на Perlmonks.

Мораль истории: будьте осторожны с оператором <>.

FWIW, я только что подтвердил, что это все еще проблема с perl 5.10.0.

Ответ 3

Вы можете указать более одного предложения -e. Иногда у меня есть командная строка, которая начинает расти, когда я уточняю операцию поиска/выделения/манипуляции. если вы что-то ошиблите, вы получите "номер строки", сообщающий вам, у которого есть ошибка.

Конечно, некоторые могут утверждать, что если у вас более одного-двух предложений, возможно, вы должны поместить все, что есть в script, но некоторые вещи просто просто выбрасываются, так зачем беспокоиться.

perl -n -e 'if (/good/)' -e '{ system "echo $_ >> good.txt"; }' \
-e 'elsif (/bad/)' -e '{ system "echo $_ >> bad.txt"; }' \
-e 'else' -e '{ system "echo $_ >> ugly.txt"; }' in.txt another.txt etc.txt

Предположительно, вы бы сделали что-то менее тривиальное, чем grep/egrep, в 3 файла: -)

Ответ 4

Параметр -i позволяет выполнять изменения в строке:

 perl -i -pe 's/abc/def/; s/fgh/hij/' file.txt

или сохранить резервную копию:

 perl -i.bak -pe 's/abc/def/; s/fgh/hij/' file.txt

Ответ 5

Мне нравится думать о perl -n как о выборе конкретных бит ввода и perl -p как map для всех строк ввода.

Как вы заметили, возможно получить эффект -p с помощью -n, и мы можем эмулировать наоборот:

$ echo -e "1\n2\n3" | perl -pe '$_="" if $_ % 2 == 0'
1
3

Пропуск строк с next выглядит более естественным, но -p обертывает код в

LINE:
while (<>) {
    ...     # your program goes here
} continue {
    print or die "-p destination: $!\n";
}

По дизайну next запускает continue блоки:

Если существует continue BLOCK, он всегда выполняется непосредственно перед тем, как условие будет снова оцениваться. Таким образом, его можно использовать для увеличения переменной цикла, даже если цикл был продолжен с помощью инструкции next.

Коммутатор -l имеет два полезных эффекта:

  • С -n и -p, автоматически chomp каждая входная запись.
  • Установите $\, чтобы каждый print неявно добавлял терминатор.

Например, чтобы захватить первые 10 портов UDP, упомянутых в /etc/services, вы могли бы

perl -ane 'print $F[1] if $F[1] =~ /udp/' /etc/services | head

но oops:

7/udp9/udp11/udp13/udp17/udp19/udp37/udp39/udp42/ud...

лучше:

$ perl -lane 'print $F[1] if $F[1] =~ /udp/' /etc/services | head
7/udp
9/udp
11/udp
13/udp
17/udp
19/udp
37/udp
39/udp
42/udp
53/udp

Помните, что -n и -p также могут быть в строке shebang, поэтому для сохранения вышележащего oneliner в качестве script:

#! /usr/bin/perl -lan

BEGIN {
  @ARGV = ("/etc/services") unless @ARGV;
  open STDOUT, "|-", "head" or die "$0: head failed";
}

print $F[1] if $F[1] =~ /udp/

Ответ 6

Моя любимая ссылка для Perl на один лайнер (и верхний хит в Google для этой фразы) охватывает perl -ne: http://novosial.org/perl/one-liner/

Ответ 7

Я часто использую sed или awk, но мне очень нравится эта функция perl для сопоставления шаблонов шаблонов:

$ cat my-input.txt
git 111 HERE 2222 voila 333
any 444 HERE none start 555 HERE 6
svn 777 aaaa 8888 nothing
two 222 HERE 9999 HERE 0000

$ perl -nle 'print $a if (($a)=/HERE ([0-9]+)/)' my-input.txt
2222
6
9999