Pergunta

Ao usar os.system() muitas vezes é necessário escapar de nomes de arquivos e outros argumentos passados ​​como parâmetros para comandos.Como posso fazer isso?De preferência, algo que funcione em vários sistemas operacionais/shells, mas em particular para o bash.

Atualmente estou fazendo o seguinte, mas tenho certeza de que deve haver uma função de biblioteca para isso, ou pelo menos uma opção mais elegante/robusta/eficiente:

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)))

Editar: Aceitei a resposta simples de usar aspas, não sei por que não pensei nisso;Acho que é porque vim do Windows, onde ' e " se comportam de maneira um pouco diferente.

Em relação à segurança, entendo a preocupação, mas, neste caso, estou interessado em uma solução rápida e fácil que os.system() fornece, e a origem das strings não é gerada pelo usuário ou pelo menos inserida por um usuário confiável (eu).

Foi útil?

Solução

Isto é o que eu uso:

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

O shell sempre aceitará um nome de arquivo entre aspas e removerá as aspas antes de passá-lo para o programa em questão.Notavelmente, isso evita problemas com nomes de arquivos que contêm espaços ou qualquer outro tipo de metacaractere de shell desagradável.

Atualizar:Se você estiver usando Python 3.3 ou posterior, use shlex.quote em vez de rolar o seu próprio.

Outras dicas

shlex.quote() faz o que você quer desde python 3.

(Usar pipes.quote para suportar python 2 e python 3)

Talvez você tenha um motivo específico para usar os.system().Mas se não, você provavelmente deveria estar usando o subprocess módulo.Você pode especificar os tubos diretamente e evitar o uso do shell.

O seguinte é de 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]

Talvez subprocess.list2cmdline é um tiro melhor?

Observe que pipes.quote está realmente quebrado no Python 2.5 e no Python 3.1 e não é seguro para uso - ele não lida com argumentos de comprimento zero.

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

Ver Problema Python 7476;foi corrigido no Python 2.6 e 3.2 e mais recentes.

Acredito que os.system apenas invoca qualquer shell de comando configurado para o usuário, então não acho que você possa fazer isso de maneira independente de plataforma.Meu shell de comando pode ser qualquer coisa, desde bash, emacs, ruby ​​ou até mesmo quake3.Alguns desses programas não esperam o tipo de argumento que você está passando para eles e, mesmo que esperassem, não há garantia de que escaparão da mesma maneira.

Perceber:Esta é uma resposta para Python 2.7.x.

De acordo com fonte, pipes.quote() é uma maneira de "Cite uma string de forma confiável como um único argumento para /bin/sh".(Embora seja obsoleto desde a versão 2.7 e finalmente exposto publicamente em Python 3.3 como o shlex.quote() função.)

Sobre A outra mão, subprocess.list2cmdline() é uma maneira de "Traduza uma sequência de argumentos em uma string de linha de comando, usando as mesmas regras do Tempo de execução do MS C".

Aqui estamos, a forma independente de plataforma de citar strings para linhas de comando.

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)

Uso:

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

# Quote multiple arguments
my_args = ['This', 'is', 'my arguments']
print quote_args(my_args)

A função que uso é:

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

aquilo é:Eu sempre coloco o argumento entre aspas duplas e, em seguida, coloco aspas invertidas nos únicos caracteres especiais entre aspas duplas.

Se você usar o comando do sistema, eu tentaria colocar na lista de permissões o que entra na chamada os.system().Por exemplo..

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

O módulo subprocess é uma opção melhor, e eu recomendaria tentar evitar usar algo como os.system/subprocess sempre que possível.

A verdadeira resposta é:Não use os.system() em primeiro lugar.Usar subprocess.call em vez disso, forneça os argumentos sem escape.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top