355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Уильям Шоттс » Командная строка Linux » Текст книги (страница 16)
Командная строка Linux
  • Текст добавлен: 12 апреля 2017, 12:30

Текст книги "Командная строка Linux"


Автор книги: Уильям Шоттс


Жанр:

   

ОС и Сети


сообщить о нарушении

Текущая страница: 16 (всего у книги 30 страниц)

[me@linuxbox ~]$ rsync -av playground foo

Мы добавили два параметра: -a (для архивирования – обеспечивает рекурсивный обход и сохранение атрибутов файлов) и -v (подробный вывод), чтобы отразить каталог playground в каталог foo. В процессе выполнения команды можно просматривать список копируемых файлов и каталогов. В конце программа выведет итоговое сообщение, как показано ниже, включающее общий объем скопированных данных:

sent 135759 bytes  received 57870 bytes  387258.00 bytes/sec

total size is 3230  speedup is 0.02

Если теперь запустить команду еще раз, результат будет другой:

[me@linuxbox ~]$ rsync -av playgound foo

building file list ... done

sent 22635 bytes  received 20 bytes  45310.00 bytes/sec

total size is 3230  speedup is 0.14

Обратите внимание на отсутствие списка файлов. Это объясняется тем, что программа rsync не обнаружила различий между ~/playground и ~/foo/playground и поэтому ничего не скопировала. Если теперь изменить файл в playground и запустить rsync еще раз, она обнаружит изменившийся файл и скопирует только его.

[me@linuxbox ~]$ touch playground/dir-099/file-Z

[me@linuxbox ~]$ rsync -av playground foo

building file list ... done

playground/dir-099/file-Z

sent 22685 bytes  received 42 bytes  45454.00 bytes/sec

total size is 3230  speedup is 0.14

В качестве примера представьте воображаемый внешний жесткий диск, использовавшийся выше с командой tar. Если после подключения такого диска к системе он снова будет смонтирован в каталог /media/BigDisk, выполним первое резервное копирование системы, для начала создав каталог /backup на внешнем устройстве, а затем вызвав rsync для копирования наиболее важных компонентов системы на внешнее устройство:

[me@linuxbox ~]$ mkdir /media/BigDisk/backup

[me@linuxbox ~]$ sudo rsync -av –delete /etc /home /usr/local /media/BigDisk/backup

В этом примере мы скопировали каталоги /etc, /home и /usr/local из нашей системы на воображаемый внешний диск. Мы добавили параметр –delete, чтобы удалить файлы, которые могут присутствовать на устройстве с резервной копией, но отсутствовать на устройстве-источнике (этот параметр не нужен при создании резервной копии в первый раз, но является полезным дополнением в последующих операциях копирования). Периодическое повторение процедуры подключения внешнего диска и запуск этой команды rsync является неплохим (хотя и не идеальным) способом сохранения резервной копии небольшой системы. Конечно, здесь также могло бы пригодиться создание псевдонима. Определим псевдоним и добавим его в свой файл .bashrc, чтобы обеспечить возможность быстрого резервного копирования:

alias backup='sudo rsync -av –delete /etc /home /usr/local /media/BigDisk/backup'

Теперь, чтобы выполнить всю работу, достаточно просто подключить внешний диск и ввести команду backup.

Использование rsync для копирования по сети

Одно из самых больших достоинств rsync – возможность копирования файлов по сети, об этом нам «говорит» буква r в названии rsync, что означает remote (удаленная). Удаленную синхронизацию можно выполнить одним из двух способов.

Первый можно использовать с удаленными системами, где установлена rsync и программа удаленной командной оболочки, такая как ssh. Допустим, что в локальной сети имеется другая система с огромным объемом дискового пространства и мы хотели бы использовать эту систему для хранения резервной копии вместо внешнего диска. Если допустить, что в этой системе уже имеется каталог /backup, куда можно было бы сохранить наши файлы, мы могли бы выполнить резервное копирование так:

[me@linuxbox ~]$ sudo rsync -av –delete –rsh=ssh /etc /home /usr/local remotesys:/backup

Мы внесли два изменения в команду, чтобы обеспечить копирование по сети. Во-первых, добавили параметр –rsh=ssh, который требует от rsync использовать в качестве удаленной командной оболочки программу ssh. Благодаря этому для передачи данных из локальной системы в удаленную мы можем использовать шифрованный туннель SSH. Во-вторых, мы добавили имя удаленного узла (в данном примере remote-sys) перед именем удаленного каталога.

Второй способ использования rsync для синхронизации файлов по сети заключается в использовании сервера rysnc. rsync можно настроить на работу в режиме демона, принимающего входящие запросы на синхронизацию. Этот прием часто используется для зеркалирования удаленных систем. Например, компания Red Hat Software поддерживает огромный репозиторий программных пакетов, разрабатываемых для ее дистрибутива Fedora. Для специалистов, занимающихся тестированием программного обеспечения, очень удобно иметь зеркало этой коллекции в ходе этапа тестирования, предшествующего этапу выпуска дистрибутива. Поскольку файлы в репозитории обновляются достаточно часто (порой по нескольку раз в день), неплохо было бы организовать периодическую синхронизацию локального зеркала вместо копирования всего объема репозитория. Один из таких репозиториев хранится в университете Georgia Tech; мы могли бы создать его зеркало с помощью локальной программы rsync и сервера rsync в Georgia Tech:

[me@linuxbox ~]$ mkdir fedora-devel

[me@linuxbox ~]$ rsync -av -delete rsync://rsync.gtlib.gatech.edu/fedora-linux-core/development/i386/os fedora-devel

В этом примере мы использовали идентификатор URI удаленного сервера rsync, включающий протокол (rsync://), имя удаленного узла (rsync.gtlib.gatech.edu) и путь к репозиторию.

19. Регулярные выражения

В следующих нескольких главах мы познакомимся с инструментами для работы с текстом. Как вы уже знаете, текстовые данные играют важную роль в Unix-подобных системах, таких как Linux. Но прежде чем переходить к исследованию возможностей этих инструментов, необходимо познакомится с технологией, которая часто ассоциируется с самыми сложными случаями использования этих инструментов – регулярными выражениями.

Знакомясь со свойствами и особенностями командной строки, мы уже встречали некоторые по-настоящему таинственные свойства и команды, такие как механизмы подстановки и экранирования, короткие комбинации клавиш и история команд, не говоря уже о редакторе vi. Регулярные выражения продолжают этот список и являются (пожалуй) самым загадочным из всех инструментов. Это не означает, что время на их изучение будет потрачено впустую. Как раз наоборот. Хорошее понимание регулярных выражений позволит вам творить настоящие чудеса, хотя истинная их ценность поначалу может быть и не очевидна.

Что такое регулярные выражения?

Регулярные выражения – это всего лишь символическая форма записи, используемая для идентификации шаблонов в тексте. Они, до определенной степени, напоминают групповые символы, используемые командной оболочкой для выбора соответствующих файлов и путей, но в более широком масштабе. Регулярные выражения поддерживаются многими инструментами командной строки и большинством языков программирования, чтобы упростить решение задач, связанных с обработкой текста. Однако проблема в том, что не все регулярные выражения одинаковы; разные инструменты и языки программирования используют собственные «диалекты» регулярных выражений. Для целей нашего обсуждения мы ограничимся регулярными выражениями, как они определены в стандарте POSIX (и поддерживаются большинством инструментов командной строки) в противоположность многим языкам программирования (особенно это относится к Perl), где используются более широкие и богатые формы записи.

grep – поиск в тексте

При работе с регулярными выражениями мы в основном будем использовать нашу старую добрую приятельницу – программу grep. Название grep в действительности произошло от фразы «global regular expression print» (глобальный поиск с помощью регулярного выражения и вывод), то есть, как видите, grep имеет некоторое отношение к регулярным выражениям. В сущности, grep просматривает текстовые файлы в поисках совпадений с указанным регулярным выражением и выводит в стандартный вывод все строки с такими совпадениями.

До сих пор мы передавали программе grep фиксированные строки, например:

[me@linuxbox ~]$ ls /usr/bin | grep zip

Эта команда выведет список всех файлов из каталога /usr/bin, имена которых содержат подстроку zip.

Программа grep имеет следующий синтаксис:

grep [параметры] регулярное_выражение [файл...]

В табл. 19.1 перечислены наиболее часто используемые параметры grep.

Таблица 19.1. Параметры команды grep

Параметр

Описание

–i

Игнорировать регистр символов. Требует не различать символы верхнего и нижнего регистра. Аналогично работает параметр –ignore-case

–v

Инвертировать критерий. Обычно grep выводит строки с совпадениями. Этот параметр заставляет grep выводить строки, не содержащие совпадений. Аналогично работает параметр –invert-match

–c

Вывести число совпадений (или «несовпадений») в присутствии параметра -v вместо самих текстовых строк. Аналогично работает параметр –count

–l

Вместо строк с совпадениями выводить только имена файлов с найденными строками. Аналогично работает параметр –files-with-matches

–L

Действует подобно параметру -l, но выводит только имена файлов, где не найдено ни одного совпадения. Аналогично работает параметр –files-without-match

–n

В начале каждой строки с совпадением вывести ее номер в файле. Аналогично работает параметр –line-number

–h

Подавить вывод имен файлов при поиске по нескольким файлам. Аналогично работает параметр –no-filename

Давайте создадим несколько текстовых файлов, чтобы наше исследование grep стало более предметным:

[me@linuxbox ~]$ ls /bin > dirlist-bin.txt

[me@linuxbox ~]$ ls /usr/bin > dirlist-usr-bin.txt

[me@linuxbox ~]$ ls /sbin > dirlist-sbin.txt

[me@linuxbox ~]$ ls /usr/sbin > dirlist-usr-sbin.txt

[me@linuxbox ~]$ ls dirlist*.txt

dirlist-bin.txt   dirlist-sbin.txt   dirlist-usr-sbin.txt

dirlist-usr-bin.txt

Ниже показано, как выполнить простой поиск в нашем списке файлов:

[me@linuxbox ~]$ grep bzip dirlist*.txt

dirlist-bin.txt:bzip2

dirlist-bin.txt:bzip2recover

В этом примере grep просматривает все перечисленные файлы в поисках строки bzip и находит два совпадения, оба в файле dirlist-bin.txt. Если бы нам достаточно было получить только имена файлов с совпадениями, а не сами совпадения, мы могли бы добавить параметр -l:

[me@linuxbox ~]$ grep -l bzip dirlist*.txt

dirlist-bin.txt

Напротив, получить список файлов, не содержащих совпадений, можно так:

[me@linuxbox ~]$ grep -L bzip dirlist*.txt

dirlist-sbin.txt

dirlist-usr-bin.txt

dirlist-usr-sbin.txt

Метасимволы и литералы

Несмотря на то что пока это не очевидно, во всех своих попытках поиска с помощью grep мы использовали регулярные выражения, хотя и очень простые. Регулярное выражение bzip, к примеру, означает, что ему соответствуют только строки в файлах, содержащие не менее четырех символов и среди этих символов присутствуют символы b, z, i и p, следующие именно в таком порядке и между ними отсутствуют какие-либо другие символы. Символы в строке bzip – это литеральные символы, то есть они соответствуют сами себе. Помимо литералов регулярные выражения могут содержать метасимволы, они используются для определения более сложных критериев сопоставления. К метасимволам регулярных выражений относятся следующие символы:

^ $ . [ ] { } – ? * + ( ) |

Все остальные символы считаются литералами. Впрочем, в некоторых случаях символ обратного слеша используется для создания метапоследовательностей, а также для экранирования метасимволов, чтобы они могли интерпретироваться как литералы, а не как метасимволы.

ПРИМЕЧАНИЕ

Как видите, многие метасимволы регулярных выражений имеют также специальное значение для механизма подстановки командной оболочки. Поэтому, передавая регулярные выражения с метасимволами в виде аргументов командной строки, следует заключать их в кавычки, чтобы предотвратить попытки командной оболочки выполнить подстановку вместо них.

Любой символ

Первый метасимвол, который мы рассмотрим, – это символ точки, соответствующий любому символу. Если включить его в регулярное выражение, он будет соответствовать любому символу в данной позиции. Например:

[me@linuxbox ~]$ grep -h '.zip' dirlist*.txt

bunzip2

bzip2

bzip2recover

gunzip

gzip

funzip

gpg-zip

preunzip

prezip

prezip-bin

unzip

unzipsfx

Здесь выполнен поиск в наших файлах совпадений с регулярным выражением .zip. В полученных результатах имеется пара важных моментов, которые необходимо отметить. Обратите внимание, что программа zip не была найдена. Это объясняется включением в регулярное выражение метасимвола точки, увеличившим длину обязательного совпадения до четырех символов; так как в имени программы zip всего три символа, оно не было найдено. Кроме того, если бы в наших списках имелись имена файлов с расширением .zip, они также были бы найдены, потому что символ точки в расширении файла интерпретировался бы как «любой символ».

Якоря

Символ крышки (^) и знак доллара ($) в регулярных выражениях интерпретируются как якоря. Это означает, что в их присутствии совпадение с регулярным выражением возможно, только если совпадение будет найдено в начале строки (^) или в ее конце ($).

[me@linuxbox ~]$ grep -h '^zip' dirlist*.txt

zip

zipcloak

zipgrep

zipinfo

zipnote

zipsplit

[me@linuxbox ~]$ grep -h 'zip$' dirlist*.txt

gunzip

gzip

funzip

gpg-zip

preunzip

prezip

unzip

zip

[me@linuxbox ~]$ grep -h '^zip$' dirlist*.txt

zip

Здесь выполняется поиск в списке файлов строки zip, находящейся в начале строки, в конце строки и занимающей всю строку, от начала до конца. Обратите внимание, что регулярное выражение ^$ (начало и конец без каких-либо символов между ними) будет соответствовать пустым строкам.

в помощь любителям кроссвордов

Моя супруга обожает разгадывать кроссворды и иногда просит меня помочь с ответом на какой-нибудь вопрос. Например: «Слово из пяти букв, третья j, последняя r, которое означает...» Подобные вопросы навели меня на размышления.

Знаете ли вы, что в вашей системе Linux имеется словарь? Загляните в каталог /usr/share/dict, и вы обнаружите там один или несколько словарей. Файлы словарей, находящиеся в каталоге, – это обычные длинные списки слов, по одному в строке, упорядоченные по алфавиту. В моей системе файл words содержит больше 98 500 слов. Найти возможные ответы на вопрос в кроссворде можно с помощью следующей команды:

[me@linuxbox ~]$ grep -i '^..j.r$' /usr/share/dict/words

Major

major

Это регулярное выражение помогает найти в файле словаря все слова, длиной в пять букв, где третья буква – j и последняя – r.

Выражения в квадратных скобках и классы символов

В дополнение к возможности описать в регулярном выражении совпадение с любым символом в заданной позиции с помощью выражения в квадратных скобках можно также описать совпадение с одним символом из определенного множества. Выражение в квадратных скобках помогает определить множество символов (включая символы, которые иначе интерпретировались бы как метасимволы), которые находятся в данной позиции. В следующем примере используется множество из двух символов, благодаря которому обнаруживаются соответствия с последовательностями bzip и gzip:

[me@linuxbox ~]$ grep -h '[bg]zip' dirlist*.txt

bzip2

bzip2recover

gzip

Множество может содержать любое число символов. Метасимволы, заключенные в квадратные скобки, теряют свое специальное значение. Лишь два метасимвола интерпретируются особым образом, но при этом они имеют иной смысл. Первый – символ крышки (^), который используется для обозначения отрицания; второй – дефис (-), который используется для обозначения диапазона символов.

Отрицание

Если сразу после открывающей квадратной скобки стоит символ крышки (^), остальные символы множества интерпретируются как недопустимые в данной позиции. Проверим это, изменив предыдущий пример:

[me@linuxbox ~]$ grep -h '[^bg]zip' dirlist*.txt

bunzip2

gunzip

funzip

gpg-zip

preunzip

prezip

prezip-bin

unzip

unzipsfx

Включив отрицание, мы получили список файлов, имена которых содержат последовательность zip, которой предшествует любой символ, кроме b или g. Обратите внимание, что файл zip не был найден. Символ отрицания не отменяет необходимости присутствия символа в заданной позиции, он лишь требует, чтобы символ в этой позиции не принадлежал указанному множеству.

Символ крышки обозначает операцию отрицания, только если является первым символом в выражении в квадратных скобках; в противном случае он теряет свое специальное значение и превращается в обычный символ.

Традиционные диапазоны символов

Если необходимо сконструировать регулярное выражение, которое находило бы в наших списках все файлы с именами, начинающимися с заглавной буквы, это можно выполнить следующим образом:

[me@linuxbox ~]$ grep -h '^[ABCDEFGHIJKLMNOPQRSTUVWXZY]' dirlist*.txt

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

[me@linuxbox ~]$ grep -h '^[A-Z]' dirlist*.txt

MAKEDEV

ControlPanel

GET

HEAD

POST

X

X11

Xorg

MAKEFLOPPIES

NetworkManager

NetworkManagerDispatcher

Мы сократили множество с 26 буквами до 3-символьного диапазона. Так можно выразить любой диапазон символов и даже несколько диапазонов, например, для поиска имен файлов, начинающихся с буквы или цифры:

[me@linuxbox ~]$ grep -h '^[A-Za-z0-9]' dirlist*.txt

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

[me@linuxbox ~]$ grep -h '[A-Z]' dirlist*.txt

Эта команда найдет все имена файлов, содержащие буквы верхнего регистра. С другой стороны, следующее выражение:

[me@linuxbox ~]$ grep -h '[-AZ]' dirlist*.txt

найдет все имена файлов, содержащие дефис, букву A или букву Z.

Классы символов POSIX

Традиционные диапазоны символов – простой и эффективный способ определения наборов символов. К сожалению, они могут использоваться не со всеми программами. Мы не испытывали никаких проблем с диапазонами, используя программу grep, но могли бы столкнуться с ними при использовании других программ.

Вернемся к главе 4, где демонстрировалось использование групповых символов для подстановки имен файлов. Там говорилось, что можно использовать диапазоны символов почти так же, как они используются в регулярных выражениях, но есть одна проблема:

[me@linuxbox ~]$ ls /usr/sbin/[ABCDEFGHIJKLMNOPQRSTUVWXYZ]*

/usr/sbin/MAKEFLOPPIES

/usr/sbin/NetworkManagerDispatcher

/usr/sbin/NetworkManager

(В разных дистрибутивах будут получены разные списки файлов, а в некоторых даже пустой список. Эти результаты получены в Ubuntu.) Эта команда вернула ожидаемый результат – список имен файлов, начинающихся с заглавной буквы. Но следующая команда даст совершенно другой результат (здесь приведена только часть результатов):

[me@linuxbox ~]$ ls /usr/sbin/[A-Z]*

/usr/sbin/biosdecode

/usr/sbin/chat

/usr/sbin/chgpasswd

/usr/sbin/chpasswd

/usr/sbin/chroot

/usr/sbin/cleanup-info

/usr/sbin/complain

/usr/sbin/console-kit-daemon

В чем же причина? Для этой длинной истории имеется короткая версия. Во времена, когда операционная система Unix только появилась на свет, был известен только один набор символов – ASCII, и этот факт нашел свое отражение в данной особенности. В ASCII первые 32 символа (с номерами 0–31) – это управляющие символы (такие, как табуляция, забой и возврат каретки). Следующие 32 (32–63) представляют печатаемые символы, включая большинство знаков пунктуации и цифры с нуля до девяти. Следующие 32 (с номерами 64–95) представляют буквы верхнего регистра и несколько знаков пунктуации. Последние 31 (с номерами 96–127) представляют буквы нижнего регистра и еще несколько знаков пунктуации. Опираясь на эту классификацию, системы, использующие набор ASCII, придерживались следующего порядка сопоставления:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz

Этот порядок отличается от лексикографического, который выглядит так:

aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ

С ростом популярности Unix за пределами США возникла необходимость в поддержке символов, не входящих в алфавит американского английского. Таблица ASCII была расширена до использования 8-битных символов, и в нее добавились символы с номерами 128–255, используемые во многих других языках. Для поддержки этой возможности в стандарт POSIX было введено понятие региона (locale), определяющее выбор набора символов для конкретного географического региона. Узнать, какой язык настроен в вашей системе, можно с помощью команды:

[me@linuxbox ~]$ echo $LANG

en_US.UTF-8

При проверке этой настройки POSIX-совместимые приложения используют лексикографический порядок, а не порядок следования символов в наборе ASCII. Это объясняет поведение команд, рассмотренное выше. Когда диапазон символов [A-Z] интерпретируется в лексикографическом порядке, он включает все алфавитные символы, кроме символа a в нижнем регистре, – именно это объясняет полученный результат.

Для частичного решения этой проблемы стандарт POSIX предусматривает несколько классов символов, описывающих диапазоны символов. Они перечислены в табл. 19.2.

Таблица 19.2. Название таблицы

Класс символов

Описание

[:alnum:]

Алфавитно-цифровые символы; эквивалент диапазона [A-Za-z0-9] в ASCII

[:word:]

То же, что и [:alnum:], с дополнительным символом подчеркивания (_)

[:alpha:]

Алфавитные символы; эквивалент диапазона [A-Za-z] в ASCII

[:blank:]

Включает символы пробела и табуляции

[:cntrl:]

Управляющие символы ASCII; включает символы ASCII с кодами от 0 до 31 и 127

[:digit:]

Цифры от 0 до 9

[:graph:]

Отображаемые символы; включает символы ASCII с кодами от 33 до 126

[:lower:]

Символы нижнего регистра

[:punct:]

Знаки пунктуации; эквивалент класса [-!"#$%&'()*+,./:;<=>?@[\]_`{|}~] в ASCII

[:print:]

Печатаемые символы; все символы из класса [:graph:] и пробел

[:space:]

Пробельные символы, включая пробел, табуляцию, возврат каретки, перевод строки, вертикальную табуляцию и перевод формата; эквивалент класса [ trnvf] в ASCII

[:upper:]

Символы верхнего регистра

[:xdigit:]

Символы, используемые для представления шестнадцатеричных цифр; эквивалент класса [0-9A-Fa-f] в ASCII

возврат к традиционному порядку сортировки

Есть возможность вернуть систему к традиционному (ASCII) порядку сортировки, изменив значение переменной окружения LANG. Как было показано в предыдущем разделе, переменная LANG хранит название языка и набора символов, заданных в региональных настройках. Значение этой переменной первоначально определяется в момент, когда выбирается язык установки дистрибутива Linux.

Увидеть региональные настройки можно, выполнив команду locale:

[me@linuxbox ~]$ locale

LANG=en_US.UTF-8

LC_CTYPE="en_US.UTF-8"

LC_NUMERIC="en_US.UTF-8"

LC_TIME="en_US.UTF-8"

LC_COLLATE="en_US.UTF-8"

LC_MONETARY="en_US.UTF-8"

LC_MESSAGES="en_US.UTF-8"

LC_PAPER="en_US.UTF-8"

LC_NAME="en_US.UTF-8"

LC_ADDRESS="en_US.UTF-8"

LC_TELEPHONE="en_US.UTF-8"

LC_MEASUREMENT="en_US.UTF-8"

LC_IDENTIFICATION="en_US.UTF-8"

LC_ALL=

Чтобы установить региональные настройки, обеспечивающие традиционное поведение системы Unix, присвойте переменной LANG значение POSIX:

[me@linuxbox ~]$ export LANG=POSIX

Имейте в виду, что в результате наших действий система будет использовать набор символов американского английского (точнее, ASCII), поэтому подумайте, действительно ли это то, что вам нужно.

Эти изменения можно сделать постоянными, добавив следующую строку в файл .bashrc:

export LANG=POSIX

Но даже наличие классов символов не дает удобного способа выражения частичных диапазонов, таких как [A-M].

Используя классы символов, можно повторить пример с выводом содержимого каталога и посмотреть, насколько улучшился результат.

[me@linuxbox ~]$ ls /usr/sbin/[[:upper:]]*

/usr/sbin/MAKEFLOPPIES

/usr/sbin/NetworkManagerDispatcher

/usr/sbin/NetworkManager

Но не забывайте, что это не является примером использования регулярных выражений, скорее это пример того, как командная оболочка выполняет подстановку путей. Мы рассмотрели его лишь потому, что классы символов POSIX поддерживаются и там и там.

Простые и расширенные регулярные выражения POSIX

Как раз когда, казалось бы, проблема путаницы с диалектами регулярных выражений решена, обнаруживается, что стандарт POSIX также делит реализации регулярных выражений на два вида: простые регулярные выражения (Basic Regular Expressions, BRE) и расширенные регулярные выражения (Extended Regular Expressions, ERE). Особенности, рассматривавшиеся до сих пор, поддерживаются всеми POSIX-совместимыми приложениями и приложениями, реализующими BRE. Программа grep – одна из них.

Чем отличаются BRE и ERE? Различия касаются наборов метасимволов. В диалекте BRE распознаются следующие метасимволы: ^ $ . [ ] *. Все остальные считаются литералами. В ERE во множество метасимволов (с соответствующими им функциями) добавляются: ( ) { } ? + |.

Однако (что самое интересное) символы ( ) { } интерпретируются в BRE как метасимволы, если они экранированы символом обратного слеша, тогда как в ERE присутствие обратного слеша перед этими же метасимволами превращает их в литералы.

Поскольку далее в этой главе мы рассмотрим особенности, являющиеся частью ERE, необходимо использовать другую версию grep. Традиционно диалект ERE поддерживался программой egrep, но GNU-версия grep также поддерживает расширенные регулярные выражения при вызове ее с параметром -E.

posix

На протяжении 1980-х система Unix обрела популярность как коммерческая операционная система, но до 1988-го в мире Unix царила полная анархия. Многие производители компьютеров лицензировали исходный код Unix у ее создателя – компании AT&T и поставляли разные версии операционной системы вместе со своими машинами. Однако в стремлении к дифференциации продуктов каждый производитель добавлял свои, патентованные изменения и расширения. В результате значительно ухудшилась совместимость программного обеспечения. Как обычно, производители пытались играть в игру, победой в которой было «замыкание» клиентов на конкретном производителе. Этот период истории Unix ныне известен как Балканизация (Balkanization).

В середине 1980-х институт инженеров электроники и электротехники (Institute of Electrical and Electronics Engineers, IEEE) начал разработку единого пакета стандартов, которые должны были определить особенности работы системы Unix (и Unix-подобных). Эти стандарты, формально известные как IEEE 1003, определяют прикладные программные интерфейсы (Application Programming Interface, API), командную оболочку и утилиты, которые должны присутствовать в стандартной Unix-подобной системе. Название POSIX, сокращенное от «Portable Operating System Interface» (интерфейс переносимой операционной системы, где буква X добавлена для лучшего звучания), было предложено Ричардом Столлманом (да, тем самым Ричардом Столлманом) – и принято IEEE.

Чередование

Первой особенностью расширенных регулярных выражений, которую мы обсудим, будет чередование (alternation, или выражение выбора) – оно позволяет выбирать совпадение с одним из нескольких выражений. Так же как выражения в квадратных скобках позволяют одному символу соответствовать множеству указанных символов, чередование позволяет находить совпадение с множеством строк или других регулярных выражений.

Для демонстрации воспользуемся комбинацией команд grep и echo. Сначала попробуем выполнить простое сопоставление строк:

[me@linuxbox ~]$ echo «AAA» | grep AAA

AAA

[me@linuxbox ~]$ echo «BBB» | grep AAA

[me@linuxbox ~]$

Достаточно простой пример, в котором мы передаем по конвейеру вывод команды echo на ввод grep и видим результат. Если обнаруживается совпадение, мы видим вывод; если совпадение отсутствует, ничего не выводится.

Теперь добавим чередование, обозначаемое метасимволом вертикальной черты:

[me@linuxbox ~]$ echo «AAA» | grep -E 'AAA|BBB'

AAA

[me@linuxbox ~]$ echo «BBB» | grep -E 'AAA|BBB'

BBB

[me@linuxbox ~]$ echo «CCC» | grep -E 'AAA|BBB'

[me@linuxbox ~]$

Здесь мы видим регулярное выражение 'AAA|BBB', которое означает «совпадение со строкой AAA или со строкой BBB». Так как это расширенная особенность, мы добавили в команду grep параметр -E (вместо этого можно было бы использовать программу egrep) и заключили регулярное выражение в кавычки, чтобы предотвратить интерпретацию командной оболочкой символа вертикальной черты как оператора конвейера. В чередовании может быть более двух вариантов:

[me@linuxbox ~]$ echo «AAA» | grep -E 'AAA|BBB|CCC'

AAA

Для объединения с другими элементами регулярного выражения чередование можно заключать в круглые скобки ():

[me@linuxbox ~]$ grep -Eh '^(bz|gz|zip)' dirlist*.txt

Этому выражению будут соответствовать имена файлов из наших списков, начинающиеся с bz, gz или zip. Если отбросить круглые скобки, смысл регулярного выражения изменится, и ему будут соответствовать имена, начинающиеся с bz или содержащие gz или zip:

[me@linuxbox ~]$ grep -Eh '^bz|gz|zip' dirlist*.txt

Квантификаторы

Расширенные регулярные выражения поддерживают несколько способов определения числа совпадений с элементом.

? – совпадение с элементом ноль или один раз

Этот квантификатор фактически означает: «совпадение с предыдущим элементом не обязательно». Представьте, что нужно проверить допустимость номера телефона, и предполагается, что номер допустим, если представлен в одной из двух форм: (nnn) nnn-nnnn или nnn nnn-nnnn, где n – это цифра. Для проверки можно было бы использовать следующее регулярное выражение:

^(?[0-9][0-9][0-9])? [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$

В этом выражении за круглыми скобками следуют знаки вопроса, указывающие, что скобки могут либо отсутствовать, либо присутствовать один раз. И снова, поскольку круглые скобки считаются метасимволами (в ERE), мы экранировали их обратными слешами, чтобы они интерпретировались как литералы.

Попробуем применить это выражение:

[me@linuxbox ~]$ echo «(555) 123-4567» | grep -E '^(?[0-9][0-9][0-9])? [0-9][0-9][0-9]$'

(555) 123-4567

[me@linuxbox ~]$ echo «555 123-4567» | grep -E '^(?[0-9][0-9][0-9])? [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$'

555 123-4567

[me@linuxbox ~]$ echo «AAA 123-4567» | grep -E '^(?[0-9][0-9][0-9])? [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$'

[me@linuxbox ~]$

Здесь регулярному выражению соответствуют обе формы записи номера телефона, но ему не соответствует номер, содержащий нецифровые символы.


    Ваша оценка произведения:

Популярные книги за неделю