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

Вихідний код: Lib/shlex.py


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

Модуль shlex визначає такі функції:

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

Розділіть рядок s за допомогою синтаксису, подібного до оболонки. Якщо comments має значення False (за замовчуванням), розбір коментарів у вказаному рядку буде вимкнено (встановлюється атрибут commenters shlex до порожнього рядка). Ця функція за замовчуванням працює в режимі POSIX, але використовує режим, відмінний від POSIX, якщо аргумент posix має значення false.

Змінено в версії 3.12: Passing None for s argument now raises an exception, rather than reading sys.stdin.

shlex.join(split_command)

Об’єднайте маркери списку split_command і поверніть рядок. Ця функція є зворотною до split().

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

Повернене значення екранується оболонкою, щоб захистити від уразливості ін’єкції (див. quote()).

Added in version 3.8.

shlex.quote(s)

Повертає екрановану версію рядка s. Повернене значення — це рядок, який можна безпечно використовувати як один маркер у командному рядку оболонки у випадках, коли ви не можете використовувати список.

Попередження

Модуль 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 або екземпляр підкласу є об’єктом лексичного аналізатора. Аргумент ініціалізації, якщо він присутній, визначає, звідки читати символи. Це має бути файлоподібний/потоковий об’єкт із методами 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 має такі методи:

shlex.get_token()

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

shlex.push_token(str)

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

shlex.read_token()

Прочитайте необроблений токен. Ігноруйте зворотний стек і не інтерпретуйте запити джерела. (Це зазвичай не є корисною точкою входу, і задокументовано тут лише для повноти.)

shlex.sourcehook(filename)

Коли shlex виявляє вихідний запит (див. source нижче), цьому методу надається наступний маркер як аргумент і очікується, що він поверне кортеж, що складається з імені файлу та відкритого файлу, подібного об’єкт.

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

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

Цей хук відкритий, щоб ви могли використовувати його для реалізації шляхів пошуку в каталогах, додавання розширень файлів та інших хаків простору імен. Немає відповідного хука „close“, але екземпляр 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)

Цей метод генерує лідер повідомлення про помилку у форматі мітки помилки компілятора 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, і значення None в режимі POSIX.

shlex.punctuation_chars

Властивість лише для читання. Символи, які вважатимуться розділовими. Серії знаків пунктуації повертатимуться як один маркер. Однак зауважте, що перевірка семантичної достовірності не виконуватиметься: наприклад, «>>>» може бути повернуто як маркер, навіть якщо він може бути не розпізнаний як такий оболонками.

Added in version 3.6.

Правила розбору

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

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

  • Екран-символи не розпізнаються;

  • Укладання символів у лапки зберігає буквальне значення всіх символів у лапках;

  • Закриваючі лапки відокремлюють слова ("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.)