Что каждый хакер 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
?
Ответы
Ответ 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