В простейших случаях perl можно использовать в командной строке как замену grep и sed, например:
perl -ne 'print if /foo/'
perl -pe 's/foo/bar/'
Но существует ряд интересных особенностей, которые часто упускаются из виду:
Опция "-l"
При добавлении опции "-l" perl автоматически очищает символ перевода строки
перед обработкой в скрипте и добавляет его при каждом выводе данных.
Например, для очистки завершающих каждую строку файла пробелов можно использовать:
perl -lpe 's/\s*$//'
(если указать perl -pe 's/\s*$//', то будут удалены и символы перевода строки)
Опция "-0"
По умолчанию perl разбивает входящий поток на строки, обрабатывая каждую строку
отдельно. Опция "-0" позволяет выполнить операцию над файлом целиком, без
разбиения на строки по символу перевода строки, а с разбиением на блоки по
нулевому символу (так как в текстовых файлах \0 не встречается можно
использовать -0 для обработки всего файла разом).
Например, для удаления из текущей директории всех файлов, имена которых
начинаются с тильды, можно использовать:
find . -name '*~' -print0 | perl -0ne unlink
Опция "-i"
При указании "-i" perl считывает поток данных из указанного в командной строке
файла, а затем записывает в него же результат работы, заменяя его. В качестве
аргумента можно указать расширение для создания резервной копии файла.
Например для удаления всех комментариев в скрипте script.sh можно использовать:
perl -i.bak -ne 'print unless /^#/' script.sh
На случай ошибки, старая версия файла будет сохранена в script.sh.bak.
Оператор ".."
Для оперировании с диапазоном строк необходимо учитывать состояние прошлых
вычислений, для чего можно использовать оператор "..".
Например, для раздельной выборки всех GPG-ключей из одного файла, выводя только
данные, идущие между указанным заголовком и футером, можно использовать:
perl -ne 'print if /-----BEGIN PGP PUBLIC KEY BLOCK-----/../-----END PGP PUBLIC KEY BLOCK-----/' FILE
Опция "-a"
При указании опции "-a" perl автоматически разбивает каждую строку на части, по
умолчанию используя пробел в качестве разделителя, и помещает ее элементы в
массив @F.
Например, для вывода 8 и 2 столбца можно использовать:
ls -l | perl -lane 'print "$F[7] $F[1]"'
Опция "-F"
Опция "-F" позволяет указать символ разделителя для разбиения строки при использовании опции "-a".
Например, для разбиения не по пробелу, а по двоеточию, нужно указать:
perl -F: -lane 'print $F[0]' /etc/passwd
Оператор "\K"
При указании "\K" внутри регулярного выражения, можно отбросить все ранее
найденные совпадения, что позволяет упростить операции по замене данных без
задействования переменных.
Например, для замены поля "From:" в тексте email можно использовать:
perl -lape 's/(^From:).*/$1 Nelson Elhage <nelhage\@ksplice.com>/'
Который можно свести к
perl -lape 's/^From:\K.*/ Nelson Elhage <nelhage\@ksplice.com>/'
уточнив, что мы не хотим заменять начало строки.
Хэш %ENV
К любой переменной системного окружения можно получить доступ через хэш %ENV,
что можно использовать для выполнения операций с одинарными кавычками,
использованию которых мешают проблемы с экранированием данного символа в shell.
Для задачи вывода имен пользователей, содержащих апостроф можно использовать:
perl -F: -lane 'print $F[0] if $F[4] =~ /'"'"'/' /etc/passwd
но считать кавычки задача неприятная, поэтому данную строку можно свести к:
env re="'" perl -F: -lane 'print $F[0] if $F[4] =~ /$ENV{re}/' /etc/passwd
Конструкции "BEGIN" и "END"
Блоки BEGIN { ... } и END { ... } позволяют организовать выполнение кода до и
после цикличной обработки строк файла.
Например, для подсчета суммы второго столбца в CSV файле можно использовать:
perl -F, -lane '$t += $F[1]; END { print $t }'
Опция "-MRegexp::Common"
Через указание опции "-M" можно загрузить любой дополнительный perl-модуль. В
однострочных скриптах удобно использовать модуль Regexp::Common, содержащий
коллекцию типовых регулярных выражений для обработки различных видов данных.
Например, для разбора вывода команды ifconfig можно использовать готовые маски
для определения IP-адресов:
ip address list eth0 | \
perl -MRegexp::Common -lne 'print $1 if /($RE{net}{IPv4})/'
|