Выпуск 18. Август 2014

Отчет о хакатоне 19-20 июля | Содержание | Использование портов GPIO в Raspberry Pi. Часть 2

Однострочники в Perl

Самый стандартный и часто используемый вариант написания кода на Perl — это создание текстового файла с кодом программы. Например, можно создать текстовый файл hello.pl вот с таким содержанием:

#!/usr/bin/perl

print "Hello\n";

И можно выполнить код этой программы в консоли:

$ perl hello.pl
Hello

Но иногда бывает очень удобно не писать отдельную программу в текстовом файле, а сразу написать в консоли в одну строчку весь код, который мы хотим выполнить. Такие команды называются однострончки (по английски — oneliner). Например:

$ perl -e 'print "Hello\n"'
Hello

Ключ -e и -E

Собственно говоря, ключ -e — это самый важный ключ для написания однострочников. Все что угодно можно сделать только с помощью этого ключа. Все остальные ключи были добавлены исключительно для упрощения написания однострочников.

Ключ -e — это выполнить код, который находится сразу после этого ключа. Например, сложить два числа:

$ perl -e 'print 2+5 . "\n"'
7

Чтобы не писать каждый раз символ для перевода на новую строку \n очень удобно использовать вместо оператора print оператор say — оператор say сам делает переход на новую строку, а еще say удобнее писать, так как это слово короче чем слово print. Но для того, чтобы оператор say можно было использовать, нужно сказать:

$ perl -e 'use feature "say"; say 2+5'
7

Если не указать use feature "say";, то мы получим ошибку:

$ perl -e 'say 2+5'
Number found where operator expected at -e line 1, near "say 2"
        (Do you need to predeclare say?)
syntax error at -e line 1, near "say 2"
Execution of -e aborted due to compilation errors.

Но писать каждый раз use feature "say"; слишком утомительно, поэтому появился новый ключ -E. Его можно использовать вместо -e, и он включит все новые штуки, которые есть в Perl:

$ perl -E 'say 2+5'
7

Ключ -M

Perl — замечательный язык, очень много в нем есть из коробки, но Perl очень силен тем, что у него есть куча библиотек. Например, вот однострочник, который скачивает веб-страницу, считает сколько на этой странице символов и выводит это число:

$ perl -E 'use LWP::Simple; say length get("http://ivan.bessarabov.ru")'
6096

Ключ -M позволяет упростить подключение библиотеки к однострочнику. Вот как выглядит эта же команда с ключом -M:

$ perl -MLWP::Simple -E 'say length get("http://ivan.bessarabov.ru")'
6096

Вот еще один пример. Хочу вывести десятый элемент последовательности Фибоначчи. Вот как выглядит однострочник без ключа -M (модулю Math::Fibonacci нужно ясно указывать, какие функции нужно добавить в пространство имен):

$ perl -E 'use Math::Fibonacci qw(term); say term 10'
55

А вот более простой вариант этого однострочника с ключом -M:

$ perl -MMath::Fibonacci=term -E 'say term 10'
55

Ключ -n

Иногда хочется не просто что-то посчитать и вывести, а как-то обработать вывод других программ. И для этих целей однострочники великлепно подходят.

Например, у нас есть какая-то програма, которая создает некий вывод:

$ echo -e '1\n2\n3'
1
2
3

Мы хотим преобразовать этот вывод — возвести каждое число в квадрат. Для того, чтобы прочитать STDIN, в Perl используется бриллиантовый оператор (diamond operator) <>:

$ echo -e '1\n2\n3'| perl -E 'while (<>) { say $_ ** 2 }'
1
4
9

Для того, чтобы упростить написание подобных однострочников, был сделан ключ -n. При использовании этого ключа код, переданный в -E обрамляется:

LINE:
while (<>) {
    # your program goes here
}

И при использовании ключа -n однострочник выглядит так:

$ echo -e '1\n2\n3'| perl -nE 'say $_ ** 2'
1
4
9

butterfly operator

С ключом -n иногда используется специальный хакерский секретный оператор “бабочка” }{

Чуть изменим задачу из прошлого раздела. У нас есть скрипт, который создает вывод:

$ echo -e '1\n2\n3'
1
2
3

Нужно просумировать все числа из вывода этого скрипта.

Если бы мы решали эту задачу с помощью Perl-скрипта, то мы бы написали что-то вроде:

while (<>) {
    $sum += $_;
}

say $sum;

Мы хотим решить эту задачу с помощью однострочника. Ключ -n создает вот такой код:

LINE:
while (<>) {
    # your program goes here
}

Но нам нужно выполнить команду после того, как цикл отработает. Для этого и используется “butterfly operator”. Вообще, это никакой не оператор, а просто пара фигурных скобок. Первая фигурная скобка закрывает цикл, а вторая фигурная сбобка открывет новый блок, чтобы сохранить корректный синтаксис. Код получается таким:

LINE:
while (<>) {
    $sum += $_
}{
    say $sum
}

А однострочник выглядит вот так:

$ echo -e '1\n2\n3'| perl -nE '$sum += $_ }{ say $sum'
6

Ключ -a

Другая задача. Нужно обработать вывод команды, которая печатает на экран данные в виде таблицы:

$ echo -e 'a\t1\nb\t2\nc\t3'
a       1
b       2
c       3

Нужно отобразить все буквы из первого столбца, у которых цисло во втором столбце нечетное. То есть, должно быть:

$ echo -e 'a\t1\nb\t2\nc\t3' | perl -E ...
a
c

Вот длинный однострочник, который решает эту задачу. Разрезаем строку по пробелам и выводим первый элемент массива, если второй элемент массива — нечетное число:

$ echo -e 'a\t1\nb\t2\nc\t3' | perl -nE 'my @e = split /\s+/, $_; say $e[0] if $e[1] % 2'
a
c

Так писать, конечно, совсем не весело. Поэтому появился ключ -a, при использовании которого строка из дефолтной переменной разрезается на отдельные элементы, которые попадают в специальный массив @F. Вот как можно решить задачу с помощью ключа -a:

$ echo -e 'a\t1\nb\t2\nc\t3' | perl -naE 'say $F[0] if $F[1] % 2'
a
c

По дефолту -a разрезает строку по пробелам. Но с помощью ключа -F можно указать паттерн, по которому будет проводиться разрезание. Например. В файле /etc/passwd разделителем ялвяется доветочие:

$ cat /etc/passwd | head -n 2
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh

Вот однострочник, который находит все User ID (колонка 3 в файле /etc/passwd), в которых есть цифра 1. Скрипт выводит логин (это колонка 1) и User ID.

$ cat /etc/passwd | perl -F: -naE 'say "$F[0] $F[2]" if $F[2] =~ /1/'
daemon 1
uucp 10
proxy 13
gnats 41
libuuid 100
syslog 101
messagebus 102
ntp 103
sshd 104
vagrant 1000
statd 105
bessarabov 1001
mysql 106
redis 107
elasticsearch 108

Ну и конечно паттерн, который передается в -F, может быть регулярным выражением. Например, есть скрипт который разделяет буквы всякой ерундой, нужно убрать ерунду и отобразить только буквы через пробел:

$ echo -e 'a---b\nc__d\ne=====f'
a---b
c__d
e=====f

Вот простое и понятное и очень аккуратное решение:

$ echo -e 'a---b\nc__d\ne=====f' | perl -F'/[=_-]+/' -naE 'print "$F[0] $F[1]"'
a b
c d
e f

Резюме

Однострочники в Perl — это удобный способ очень быстро решить небольшую задачу. Для работы в консоли часто используют команды sed и awk. Но если человек умеет работать с Perl, то стоит использовать именно его.

В этой статье описаны самые базовые способы использования perl в консоли. Полное описание всех возможных способов запуска perl есть в документации perlrun.

Иван Бессарабов


Отчет о хакатоне 19-20 июля | Содержание | Использование портов GPIO в Raspberry Pi. Часть 2
Нас уже 1393. Больше подписчиков — лучше выпуски!

Комментарии к статье