Перейти к содержимому

24.3. shlex – Простой лексический анализ

Исходный код: Lib/shlex.py


Класс shlex упрощает написание лексических анализаторов для простых синтаксисов, напоминающих синтаксис оболочки Unix. Это часто бывает полезно для создания мини-языков (например, в конфигурационных файлах приложений Python) или для разбора строк в кавычках.

Модуль shlex определяет следующие функции:

shlex.split(s, comments=False, posix=True)

Разбивает строку s, используя синтаксис, подобный оболочке. Если comments равен False (по умолчанию), разбор комментариев в данной строке будет отключён (установка атрибута commenters экземпляра shlex в пустую строку). Эта функция по умолчанию работает в режиме POSIX, но использует не-POSIX режим, если аргумент posix имеет значение false.

Примечание

Поскольку функция split() создаёт экземпляр shlex, передача None в качестве s приведёт к чтению строки для разбиения из стандартного ввода.

shlex.quote(s)

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

Этот способ небезопасен:

python
>>> filename = 'somefile; rm -rf ~'
>>> command = 'ls -l {}'.format(filename)
>>> print(command)  # выполнено через оболочку: бум!
ls -l somefile; rm -rf ~

quote() позволяет закрыть эту брешь в безопасности:

python
>>> command = 'ls -l {}'.format(quote(filename))
>>> print(command)
ls -l 'somefile; rm -rf ~'
>>> remote_command = 'ssh home {}'.format(quote(command))
>>> print(remote_command)
ssh home 'ls -l '"'"'somefile; rm -rf ~'"'"''

Экранирование совместимо с оболочками UNIX и с split():

text
>>> remote_command = split(remote_command)
>>> remote_command
['ssh', 'home', "ls -l 'somefile; rm -rf ~'"]
>>> command = split(remote_command[-1])
>>> command
['ls', '-l', 'somefile; rm -rf ~']

Новое в версии 3.3.

Модуль shlex определяет следующий класс:

classshlex.shlex(instream=None, infile=None, posix=False, punctuation_chars=False)

Экземпляр shlex или экземпляр подкласса представляет собой объект лексического анализатора. Аргумент инициализации, если он указан, определяет источник чтения символов. Это должен быть объект, подобный файлу или потоку, с методами read() и readline(), либо строка. Если аргумент не передан, ввод будет производиться из sys.stdin. Второй необязательный аргумент – строка имени файла, которая задаёт начальное значение атрибута infile. Если аргумент instream опущен или равен sys.stdin, этот второй аргумент по умолчанию равен «stdin». Аргумент posix задаёт режим работы: когда posix не истинно (по умолчанию), экземпляр shlex будет работать в режиме совместимости. При работе в режиме POSIX shlex будет стараться максимально точно следовать правилам разбора оболочки POSIX. Аргумент punctuation_chars позволяет ещё сильнее приблизить поведение к тому, как разбирают реальные оболочки. Он может принимать несколько значений: значение по умолчанию, False, сохраняет поведение, принятое в Python 3.5 и более ранних версиях. Если задано значение True, то разбор символов ();<>|& изменяется: любая последовательность этих символов (считающихся знаками пунктуации) возвращается как единый токен. Если задана непустая строка символов, эти символы будут использоваться как знаки пунктуации. Любые символы из атрибута wordchars, которые присутствуют в punctuation_chars, будут удалены из wordchars. Подробнее см. раздел Улучшенная совместимость с оболочками.

Изменено в версии 3.6: Добавлен параметр punctuation_chars.

Смотрите также

Модуль configparser

Парсер для конфигурационных файлов, похожих на файлы .ini в Windows.

24.3.1. Объекты shlex

Экземпляр shlex имеет следующие методы:

shlex.get_token()

Возвращает токен. Если токены были помещены в стек с помощью push_token(), извлекает токен из стека. В противном случае читает один токен из входного потока. Если при чтении сразу встречается конец файла, возвращается eof (пустая строка ('') в не-POSIX режиме и None в режиме POSIX).

shlex.push_token(str)

Помещает аргумент в стек токенов.

shlex.read_token()

Читает необработанный токен. Игнорирует стек pushback и не обрабатывает запросы источников. (Обычно это не является полезной точкой входа и документируется здесь только для полноты.)

shlex.sourcehook(filename)

Когда shlex обнаруживает запрос источника (см. source ниже), этому методу передаётся следующий токен в качестве аргумента, и ожидается, что он вернёт кортеж из имени файла и открытого файлоподобного объекта.

Обычно этот метод сначала удаляет кавычки из аргумента. Если результат является абсолютным путём, или не было предыдущего активного запроса источника, или предыдущий источник был потоком (например, sys.stdin), результат остаётся без изменений. В противном случае, если результат является относительным путём, к нему добавляется каталог файла, находящегося непосредственно перед ним в стеке включения источников (это поведение аналогично тому, как препроцессор C обрабатывает #include "file.h").

Результат манипуляций рассматривается как имя файла и возвращается как первый компонент кортежа, при этом open() вызывается для него, чтобы получить второй компонент. (Примечание: это обратный порядок аргументов при инициализации экземпляра!)

Этот хук предоставляется, чтобы вы могли использовать его для реализации путей поиска в каталогах, добавления расширений файлов и других трюков с пространствами имён. Соответствующего хука закрытия нет, но экземпляр shlex вызовет метод close() входного потока источника, когда тот вернёт EOF.

Для более явного управления стеком источников используйте методы push_source() и pop_source().

shlex.push_source(newstream, newfile=None)

Помещает входной поток источника в стек ввода. Если указан аргумент имени файла, он будет доступен для использования в сообщениях об ошибках. Это тот же метод, используемый внутренне методом sourcehook().

shlex.pop_source()

Извлекает последний помещённый входной источник из стека ввода. Это тот же метод, используемый внутренне, когда лексер достигает EOF в стековом входном потоке.

shlex.error_leader(infile=None, lineno=None)

Этот метод генерирует префикс сообщения об ошибке в формате метки ошибки компилятора C в Unix; формат: '"%s", line %d: ', где %s заменяется именем текущего исходного файла, а %d – номером текущей строки ввода (необязательные аргументы можно использовать для переопределения этих значений).

Эта удобная функция предоставляется, чтобы побудить пользователей shlex генерировать сообщения об ошибках в стандартном, разбираемом формате, который понимают Emacs и другие инструменты Unix.

Экземпляры подклассов shlex имеют некоторые открытые переменные экземпляра, которые либо управляют лексическим анализом, либо могут использоваться для отладки:

shlex.commenters

Строка символов, которые распознаются как начало комментария. Все символы от начала комментария до конца строки игнорируются. По умолчанию включает только '#'.

shlex.wordchars

Строка символов, которые будут накапливаться в многосимвольные токены. По умолчанию включает все буквенно-цифровые символы ASCII и символ подчёркивания. В режиме POSIX также включаются символы с ударением из набора Latin-1. Если punctuation_chars не пустая, символы ~-./*?=, которые могут встречаться в именах файлов и параметрах командной строки, также будут включены в этот атрибут, а любые символы, присутствующие в punctuation_chars, будут удалены из wordchars, если они там есть.

shlex.whitespace

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

shlex.escape

Символы, которые будут считаться escape-символами. Это используется только в режиме POSIX и по умолчанию включает только '\'.

shlex.quotes

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

shlex.escapedquotes

Символы в quotes, которые будут интерпретировать escape-символы, определённые в escape. Это используется только в режиме POSIX и по умолчанию включает только '"'.

shlex.whitespace_split

Если True, токены будут разделяться только по пробелам. Это полезно, например, для разбора командной строки с помощью shlex, получая токены аналогично аргументам оболочки. Если этот атрибут равен True, punctuation_chars не будет работать, и разбиение будет происходить только по пробелам. При использовании punctuation_chars, который предназначен для разбора, более близкого к тому, что реализовано в оболочках, рекомендуется оставить whitespace_split как False (значение по умолчанию).

shlex.infile

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

shlex.instream

Входной поток, из которого этот экземпляр shlex читает символы.

shlex.source

По умолчанию этот атрибут равен None. Если присвоить ему строку, эта строка будет распознаваться как запрос включения на лексическом уровне, аналогично ключевому слову source в различных оболочках. То есть следующий за ним токен будет открыт как имя файла, и ввод будет браться из этого потока до конца файла, после чего будет вызван метод close() этого потока, и источником ввода снова станет исходный поток. Запросы источника могут складываться в стек на любую глубину.

shlex.debug

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

shlex.lineno

Номер строки источника (количество встреченных символов новой строки плюс один).

shlex.token

Буфер токена. Может быть полезен при перехвате исключений.

shlex.eof

Токен, используемый для определения конца файла. В не-POSIX режиме он устанавливается в пустую строку (''), а в POSIX-режиме – в None.

shlex.punctuation_chars

Символы, которые будут считаться пунктуацией. Последовательности знаков пунктуации будут возвращаться как один токен. Однако обратите внимание: никакой проверки семантической корректности не выполняется; например, ‘>>>’ может быть возвращён как токен, даже если оболочки не распознают его как таковой.

Новое в версии 3.6.

24.3.2. Правила разбора

При работе в режиме, отличном от POSIX, shlex будет придерживаться следующих правил.

  • Символы кавычек не распознаются внутри слов (Do"Not"Separate разбирается как одно слово Do"Not"Separate);

  • Символы экранирования не распознаются;

  • Заключение символов в кавычки сохраняет буквальное значение всех символов внутри кавычек;

  • Закрывающие кавычки разделяют слова ("Do"Separate разбирается как "Do" и Separate);

  • Если whitespace_split равно False, любой символ, не объявленный символом слова, пробельным или кавычкой, будет возвращён как односимвольный токен. Если оно равно True, shlex будет разделять слова только по пробелам;

  • Конец файла обозначается пустой строкой ('');

  • Невозможно разобрать пустые строки, даже если они заключены в кавычки.

При работе в режиме POSIX shlex будет придерживаться следующих правил разбора.

  • Кавычки удаляются и не разделяют слова ("Do"Not"Separate" разбирается как одно слово DoNotSeparate);

  • Неэкранированные символы экранирования (например, '\') сохраняют буквальное значение следующего за ними символа;

  • Заключение символов в кавычки, не входящие в escapedquotes (например, "'"), сохраняет буквальное значение всех символов внутри кавычек;

  • Заключение в кавычки символов, которые являются частью escapedquotes (например, '"'), сохраняет буквальное значение всех символов внутри кавычек, за исключением символов, упомянутых в escape. Символы экранирования сохраняют своё специальное значение, только если за ними следует используемая кавычка или сам символ экранирования. В противном случае символ экранирования считается обычным символом.

  • Конец файла обозначается значением None;

  • Пустые строки в кавычках ('') разрешены.

24.3.3. Улучшенная совместимость с оболочками

Новое в версии 3.6.

Класс shlex обеспечивает совместимость с синтаксическим анализом, выполняемым распространёнными оболочками Unix, такими как bash, dash и sh. Чтобы воспользоваться этой совместимостью, укажите аргумент punctuation_chars в конструкторе. По умолчанию используется False, что сохраняет поведение до версии 3.6. Однако если установить True, то синтаксический анализ символов ();<>|& изменится: любая последовательность этих символов возвращается как один токен. Хотя это не является полноценным синтаксическим анализатором оболочек (что выходило бы за рамки стандартной библиотеки, учитывая множество существующих оболочек), это позволяет обрабатывать командные строки проще, чем иначе. Разницу демонстрирует следующий фрагмент:

pycon3
 >>> import shlex
 >>> text = "a && b; c && d || e; f >'abc'; (def \"ghi\")"
 >>> list(shlex.shlex(text))
 ['a', '&', '&', 'b', ';', 'c', '&', '&', 'd', '|', '|', 'e', ';', 'f', '>',
 "'abc'", ';', '(', 'def', '"ghi"', ')']
 >>> list(shlex.shlex(text, punctuation_chars=True))
 ['a', '&&', 'b', ';', 'c', '&&', 'd', '||', 'e', ';', 'f', '>', "'abc'",
 ';', '(', 'def', '"ghi"', ')']

Разумеется, будут возвращаться токены, недопустимые для оболочек, и потребуется реализовать собственные проверки ошибок для возвращаемых токенов.

Вместо передачи True в качестве значения параметра punctuation_chars можно передать строку с определёнными символами, которая будет использоваться для определения того, какие символы считаются знаками препинания. Например:

python
>>> import shlex
>>> s = shlex.shlex("a && b || c", punctuation_chars="|")
>>> list(s)
['a', '&', '&', 'b', '||', 'c']

Примечание

Когда указан punctuation_chars, атрибут wordchars дополняется символами ~-./*?=. Это связано с тем, что эти символы могут встречаться в именах файлов (включая шаблоны) и аргументах командной строки (например, --color=auto). Следовательно:

python
>>> import shlex
>>> s = shlex.shlex('~/a && b-c --color=auto || d *.py?',
...                 punctuation_chars=True)
>>> list(s)
['~/a', '&&', 'b-c', '--color=auto', '||', 'd', '*.py?']

Для достижения наилучшего эффекта punctuation_chars следует устанавливать совместно с posix=True. (Обратите внимание, что posix=False является значением по умолчанию для shlex.)