shlex — Простой лексический анализ

Источник: Lib/shlex.py


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

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

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

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

Изменено в версии 3.12: Передача None для аргумента s теперь вызывает исключение, а не чтение sys.stdin.

shlex.join(split_command)

Конкатенируйте лексемы из списка split_command и верните строку. Эта функция является обратной по отношению к split().

>>> from shlex import join
>>> print(join(['echo', '-n', 'Multiple words']))
echo -n 'Multiple words'

Возвращаемое значение имеет shell-эскейп для защиты от инъекционных уязвимостей (см. quote()).

Added in version 3.8.

shlex.quote(s)

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

Предупреждение

Модуль shlex предназначен только для оболочек Unix.

Не гарантируется корректность работы функции quote() в оболочках, не совместимых с POSIX, или в оболочках других операционных систем, таких как Windows. Выполнение команд, цитируемых этим модулем, в таких оболочках может открыть возможность уязвимости для инъекции команд.

Рассмотрите возможность использования функций, передающих аргументы команд в виде списков, например subprocess.run() с shell=False.

Эта идиома была бы небезопасной:

>>> filename = 'somefile; rm -rf ~'
>>> command = 'ls -l {}'.format(filename)
>>> print(command)  # executed by a shell: boom!
ls -l somefile; rm -rf ~

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

>>> from shlex import quote
>>> 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():

>>> from shlex import split
>>> 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 ~']

Added in version 3.3.

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

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

Экземпляр shlex или экземпляр подкласса - это объект лексического анализатора. Аргумент инициализации, если он присутствует, указывает, откуда считывать символы. Это должен быть объект типа file-/stream с методами 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. Дополнительные сведения см. в разделе Улучшенная совместимость с оболочками. Атрибут punctuation_chars может быть задан только при создании экземпляра shlex и не может быть изменен позднее.

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

См.также

Модуль configparser

Парсер конфигурационных файлов, аналогичных файлам Windows .ini.

Объекты шлекса

У экземпляра shlex есть следующие методы:

shlex.get_token()

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

shlex.push_token(str)

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

shlex.read_token()

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

shlex.sourcehook(filename)

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

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

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

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

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

shlex.push_source(newstream, newfile=None)

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

shlex.pop_source()

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

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

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

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

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

shlex.commenters

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

shlex.wordchars

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

shlex.whitespace

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

shlex.escape

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

shlex.quotes

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

shlex.escapedquotes

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

shlex.whitespace_split

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

Изменено в версии 3.8: Атрибут punctuation_chars стал совместим с атрибутом whitespace_split.

shlex.infile

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

shlex.instream

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

shlex.source

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

shlex.debug

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

shlex.lineno

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

shlex.token

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

shlex.eof

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

shlex.punctuation_chars

Свойство, доступное только для чтения. Символы, которые будут считаться пунктуацией. Ряды знаков препинания будут возвращены как один токен. Однако обратите внимание, что проверка семантической валидности не производится: например, „>>>“ может быть возвращена как лексема, хотя она может быть не распознана shell’ом как таковая.

Added in version 3.6.

Правила синтаксического анализа

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

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

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

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

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

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

  • EOF сигнализируется пустой строкой ('');

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

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

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

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

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

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

  • EOF сигнализируется значением None;

  • Допускаются пустые строки в кавычках ('').

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

Added in version 3.6.

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

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

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

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

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

Примечание

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

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

Однако, чтобы максимально точно соответствовать оболочке, рекомендуется всегда использовать posix и whitespace_split при использовании punctuation_chars, что полностью исключит wordchars.

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