Как избежать вызовов os.system()?

При использовании 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(), а источник строк либо не создан пользователем, либо, по крайней мере, введенный доверенным пользователем (мной).

Ответ 1

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

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

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

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

Ответ 2

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

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

Ответ 3

Возможно, у вас есть определенная причина для использования 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]

Ответ 4

Может быть, subprocess.list2cmdline лучший снимок?

Ответ 5

Обратите внимание, что 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 issue 7476; он был исправлен в Python 2.6 и 3.2 и новее.

Ответ 6

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

Ответ 7

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

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

В с другой стороны, 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)

Ответ 8

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

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

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

Ответ 9

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

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

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

Ответ 10

Реальный ответ: Не используйте os.system() в первую очередь. Вместо этого используйте subprocess.call и поставьте необесцененные аргументы.