Что должен знать каждый 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
?
7 ответов
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
Особенно умный пример, который использует несоответствующие скобки, здесь.
Есть одна важная вещь, которую нужно знать о perl -ne
а также perl -pe
скрипты: они неявно используют <>
,
"Почему это важно?" Вы можете спросить.
Магия <>
Оператор использует форму 2 arg open. Если вы помните, 2 arg open включает спецификацию режима с именем файла в одном аргументе. Старый стиль вызова open FILE, $foo
уязвим для манипулирования файловым режимом. Особенно интересным режимом в этом контексте является |
- вы открываете дескриптор канала для процесса, который выполняете.
Вы можете подумать "Большое дело!", Но это так.
- Представьте себе задачу cron, выполняемую пользователем root для запуска файлов журналов в некотором каталоге.
- Сценарий вызывается как
script *
, - Представьте себе файл в этом каталоге с именем
|rm -rf /
,
Что просходит?
- Оболочка расширяет
*
и мы получаемscript file_1 file_2 '|rm -rf /' file_4
- Скрипт обрабатывает
file_1
а такжеfile_2
, - Затем он открывает дескриптор STDIN из
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 файла:-)
-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 -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], если $F[1] =~ /udp/' /etc/services | голова
но упс
7/udp9/udp11/udp13/udp17/udp19/udp37/udp39/udp42/ уд...
Лучше:
$ perl -lane 'print $ F [1], если $F[1] =~ /udp/' /etc/services | голова 7 / UDP 9 / UDP 11 / UDP 13 / UDP 17 / UDP 19 / UDP 37 / UDP 39 / УДП 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 one liners (и самый популярный в Google для этой фразы) охватывает perl -ne
: http://novosial.org/perl/one-liner/
Я часто использую 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