# `shlex` — Simple lexical analysis

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

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

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

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

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

  Примітка

  Since the `split()` function instantiates a `shlex` instance, passing `None` for s will read the string to split from standard input.

  Застаріло починаючи з версії 3.9: Passing `None` for s will raise an exception in future Python versions.

shlex.join(split_command)

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

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

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

  Нове в версії 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 ~']

  Нове в версії 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

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

  Нове в версії 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`;

- Допускаються порожні рядки в лапках (`''`).

## Покращена сумісність із оболонками

Нове в версії 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`.)