Я использовал командную строку Perl с a -ne
опция в течение многих лет, в основном в файлы текста процесса способами, которыми не может sed. Пример:
cat in.txt | perl -ne "s/abc/def/; s/fgh/hij/; print;" > out.txt
Я понятия не имею, где я изучил это, и только сегодня считал perlrun и нашел, что существуют другие формы (perl -pe
например).
Что еще должно я знать о perl -ne
?
perl -ne 'CODE'
эквивалентно программе
while (<>) {
CODE
}
perl -ane 'CODE'
и perl - F / PATTERN / -ane
- также хорошие идиомы, о которых следует знать. Они эквивалентны
while (<>) {
@F = split /\s+/, $_;
CODE
}
и
while (<>) {
@F = split /PATTERN/, $_;
CODE
}
. Пример: advanced 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
Особенно умный пример, в котором используются несовпадающие фигурные скобки, здесь .
Опция -i
позволяет делать изменения в строке:
perl -i -pe 's/abc/def/; s/fgh/hij/' file.txt
или сохранять резервную копию:
perl -i.bak -pe 's/abc/def/; s/fgh/hij/' file.txt
Есть одна важная вещь, которую нужно знать о скриптах perl -ne
и perl -pe
: они неявно используют <>
.
"Почему это важно?" вы можете спросить.
Магический оператор <>
использует форму open с двумя аргументами. Если вы помните, 2 arg open включает в себя спецификацию режима с именем файла в одном аргументе. Вызов старого стиля open FILE, $ foo
уязвим для манипуляций с файловым режимом. В этом контексте особенно интересен режим |
- вы открываете дескриптор канала для процесса, который вы выполняете.
Вы могли подумать: «Подумаешь!», Но это так.
сценарий *
. | rm -rf /
. Что происходит?
*
, и мы получаем скрипт file_1 file_2 '| rm -rf /' file_4
file_1
и файл_2
. rm -rf /
. file_4
больше не существует, поэтому мы не можем его открыть. Конечно, возможности безграничны.
Вы можете прочитать более подробное обсуждение этого вопроса на Perlmonks .
Мораль истории: будьте осторожны с оператором <>
.
FWIW, я только что подтвердил, что это все еще проблема с perl 5.10.0.
Вы можете указать более одного предложения -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 файла: -)
Мне нравится думать о perl -n
как о выборе определенных битов входа и perl -p
как о map
для всех строк входа.
Как вы заметили, можно получить эффект -p
с -n
, а можно эмулировать наоборот:
$ echo -e "1\n2\n3" | perl -pe '$_="" if $_ % 2 == 0' 1 3
Пропуск строк с -p
казался бы более естественным, но -p
код обёртывания в
LINE:
while (<>) {
... # your program goes here
} continue {
print or die "-p destination: $!\n";
}
По замыслу, -p
выполняется -продолжение
-блоки:
Если есть
-продолжение
BLOCK, то он всегда выполняется непосредственно перед тем, как условие снова будет вычислено. Таким образом, его можно использовать для инкремента переменной цикла, даже если цикл был продолжен с помощьюследующего оператора
.
Переключатель -l
имеет два полезных эффекта:
-n
и -p
, автоматически chomp
каждая входная запись. $\
, чтобы каждая распечатывала
неявно добавляла терминатор. Например, чтобы захватить первые 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/
Моя любимая ссылка на однострочники Perl (и первое место в Google по этой фразе) охватывает perl -ne
: http://novosial.org/perl/one-liner/