Что должен знать каждый Perl-хакер о Perl -ne? - PullRequest
18 голосов
/ 06 февраля 2010

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

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

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

Что еще я должен знать о perl -ne?

Ответы [ 7 ]

23 голосов
/ 06 февраля 2010

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


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

13 голосов
/ 07 февраля 2010

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

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

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

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

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

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

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

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

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

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

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

6 голосов
/ 06 февраля 2010

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

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

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 голосов
/ 06 февраля 2010

Опция -i позволяет вам делать изменения в строке:

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

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

 perl -i.bak -pe 's/abc/def/; s/fgh/hij/' file.txt
2 голосов
/ 06 февраля 2010

Мне нравится думать о 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 имеет два полезных эффекта:

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

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

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

но упс:

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 как скрипт:

#! /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/
1 голос
/ 21 февраля 2014

Я часто использую 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
1 голос
/ 08 февраля 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...