Как избежать вызовов os.system ()? - PullRequest
106 голосов
/ 30 августа 2008

При использовании os.system () часто необходимо экранировать имена файлов и другие аргументы, передаваемые в качестве параметров командам. Как я могу это сделать? Желательно что-то, что будет работать на нескольких операционных системах / оболочках, но особенно для bash.

В настоящее время я делаю следующее, но я уверен, что для этого должна быть библиотечная функция или, по крайней мере, более элегантный / надежный / эффективный параметр:

def sh_escape(s):
   return s.replace("(","\\(").replace(")","\\)").replace(" ","\\ ")

os.system("cat %s | grep something | sort > %s" 
          % (sh_escape(in_filename), 
             sh_escape(out_filename)))

Редактировать: Я принял простой ответ, используя кавычки, не знаю, почему я не подумал об этом; Я думаю, потому что я пришел из Windows, где «и» ведут себя немного по-другому.

Что касается безопасности, я понимаю проблему, но в этом случае меня интересует быстрое и простое решение, которое предоставляет os.system (), и источник строк либо не генерируется пользователем, либо, по крайней мере, введено доверенным пользователем (мной).

Ответы [ 10 ]

141 голосов
/ 11 мая 2009

shlex.quote() делает то, что вы хотите, начиная с Python 3.

(используйте pipes.quote для поддержки как Python 2, так и Python 3)

75 голосов
/ 30 августа 2008

Вот что я использую:

def shellquote(s):
    return "'" + s.replace("'", "'\\''") + "'"

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

Обновление : Если вы используете Python 3.3 или более позднюю версию, используйте shlex.quote вместо своего собственного.

54 голосов
/ 30 августа 2008

Возможно, у вас есть конкретная причина для использования os.system(). Но если нет, то вам, вероятно, следует использовать модуль subprocess . Вы можете указать трубы напрямую и избегать использования оболочки.

Следующее от PEP324 :

Replacing shell pipe line
-------------------------

output=`dmesg | grep hda`
==>
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]
9 голосов
/ 25 мая 2012

Может быть, subprocess.list2cmdline лучше выстрелить?

4 голосов
/ 11 декабря 2009

Обратите внимание, что pipe.quote на самом деле не работает в Python 2.5 и Python 3.1 и не безопасен в использовании - он не обрабатывает аргументы нулевой длины.

>>> from pipes import quote
>>> args = ['arg1', '', 'arg3']
>>> print 'mycommand %s' % (' '.join(quote(arg) for arg in args))
mycommand arg1  arg3

См. Выпуск Python 7476 ; это было исправлено в Python 2.6 и 3.2 и новее.

3 голосов
/ 13 апреля 2015

Примечание : это ответ для Python 2.7.x.

Согласно источнику , pipes.quote() - это способ " Надежно заключить строку в качестве единственного аргумента для / bin / sh ". (Хотя это устарело с версии 2.7 и, наконец, открыто публикуется в Python 3.3 как функция shlex.quote().)

On С другой стороны , subprocess.list2cmdline() - это способ " Перевести последовательность аргументов в строку командной строки, используя те же правила, что и MS C runtime ».

Вот мы, независимый от платформы способ цитирования строк для командных строк.

import sys
mswindows = (sys.platform == "win32")

if mswindows:
    from subprocess import list2cmdline
    quote_args = list2cmdline
else:
    # POSIX
    from pipes import quote

    def quote_args(seq):
        return ' '.join(quote(arg) for arg in seq)

Использование:

# Quote a single argument
print quote_args(['my argument'])

# Quote multiple arguments
my_args = ['This', 'is', 'my arguments']
print quote_args(my_args)
3 голосов
/ 30 августа 2008

Я считаю, что os.system просто вызывает любую командную оболочку, настроенную для пользователя, поэтому я не думаю, что вы можете сделать это независимо от платформы. Моя командная оболочка может быть любой из bash, emacs, ruby ​​или даже quake3. Некоторые из этих программ не ожидают того типа аргументов, которые вы им передаете, и даже если они это сделали, нет никакой гарантии, что они сбегут таким же образом.

1 голос
/ 04 октября 2010

Используемая мной функция:

def quote_argument(argument):
    return '"%s"' % (
        argument
        .replace('\\', '\\\\')
        .replace('"', '\\"')
        .replace('$', '\\$')
        .replace('`', '\\`')
    )

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

0 голосов
/ 25 апреля 2013

Реальный ответ: не используйте os.system() во-первых. Вместо этого используйте subprocess.call и укажите неэкранированные аргументы.

0 голосов
/ 30 августа 2008

Если вы используете системную команду, я бы попытался внести в белый список то, что входит в вызов os.system (). Например ..

clean_user_input re.sub("[^a-zA-Z]", "", user_input)
os.system("ls %s" % (clean_user_input))

Модуль подпроцесса - лучший вариант, и я бы порекомендовал стараться избегать использования чего-либо вроде os.system / subprocess, где это возможно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...