Разделить аргументы, такие как shlex.split, но преобразовать переменные среды

#python #environment-variables #shlex

#python #переменные среды #shlex

Вопрос:

Я хочу разделить строку на аргументы командной строки, точно так же, как это делает shlex.split. Однако, похоже, что shlex не преобразует переменные среды (например $USER ), и вывод делает невозможным узнать, была ли экранирована переменная среды:

 >>> print(shlex.split("My name is Alice"))
['My', 'name', 'is', 'Alice']
>>> print(shlex.split("My name is '$USER'"))
['My', 'name', 'is', '$USER']
>>> print(shlex.split("My name is $USER")) # expected Alice, not $USER
['My', 'name', 'is', '$USER']
 

Есть ли способ добиться этого? (надеюсь, без повторной реализации всего этого)

Кроме того, почему shlex.split не делает это по умолчанию в первую очередь?

Если это имеет значение, я использую Python 3.6.8.

Ответ №1:

Переданный аргумент shlex.split() является строкой.

Вам нужно будет получить переменную среды, используя os.environ , а затем объединить ее в строку, например

 import shlex
import os
print(shlex.split(f"My name is {os.environ['USER']}"))
# ['My', 'name', 'is', 'Alice']
 

Если ваша входная строка поступает из файла, вы можете оценить переменные среды с помощью os.path.expandvars() :

 import shlex
import os
print(shlex.split(os.path.expandvars("My name is $USER")))
# ['My', 'name', 'is', 'Alice']
 

Если вам нужно учитывать экранируемые переменные в строке, вы можете отправить строку echo в оболочку, используя subprocess.run() with shell set to True .

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

 import shlex
import subprocess

strings = [
    "My name is Alice",
    "My name is '$USER'",
    "My name is $USER",
    "My name is $USER"
]

for s in strings:
    split = subprocess.run(f'echo {s}', shell=True, stdout=subprocess.PIPE)
    print(shlex.split(split.stdout.decode('utf-8')))
# ['My', 'name', 'is', 'Alice']
# ['My', 'name', 'is', '$USER']
# ['My', 'name', 'is', '$USER']
# ['My', 'name', 'is', 'Alice']
 

ПРЕДУПРЕЖДЕНИЕ:

Установка shell на True опасно. Делайте это только в том случае, если входной строке доверяют.

Например, если строка была "My name is $USER; rm file" , то файл file будет удален.

Комментарии:

1. Строка была создана за пределами Python (она будет считана из файла), поэтому преобразование должно быть выполнено при ее разборе / разделении.

2. это также расширяет переменные, которые должны быть экранированы (например, второй пример, который я привел, с '$USER' )

3. @neatnit проверьте, работает ли приведенное выше обновление для вас

4. Спасибо, но, боюсь shell=True , в моем случае это не очень хорошая идея. Хотя это сработало бы.

5. В конце концов я использовал os.path.expandvars то, что вы предложили, хотя это не так надежно, как я надеялся. Это почти наверняка сработает в моем случае. Спасибо за это! Тем не менее, я не уверен, могу ли я принять этот ответ, поскольку он не ставит все галочки в моем вопросе.. Что вы думаете?