2009-06-08 23 views
25

¿Hay algo en la biblioteca estándar de Python que pueda analizar/desenredar correctamente las cadenas para usar en comandos de shell? Busco el análogo de pitón a Perl de String::ShellQuote::shell_quote:Módulo de Python para shellquote/unshellquote?

$ print String::ShellQuote::shell_quote("hello", "stack", "overflow's", "quite", "cool") 
hello stack 'overflow'\''s' quite cool 

Y, aún más importante, algo que va a trabajar en la dirección inversa (tomar una cadena y descomponerlo en una lista).

Respuesta

2

Nunca debe tener que cotizar. La forma correcta de hacer un comando es no hacer comillas de shell y en su lugar usar subprocess.call o subprocess.Popen, y pasar una lista de argumentos sin comillas. Esto es inmune a la expansión del caparazón.

decir

subprocess.Popen(['echo', '"', '$foo'], shell=False) 

Si desea Unquote datos citados shell, puede utilizar shlex.shlex así:

list(shlex.shlex("hello stack 'overflow'\''s' quite cool")) 
+10

¿Y si necesito pasar un comando (que requiere escape) para SSH para ejecutar una vez que alcanza el otro lado? –

+10

Esta no es una respuesta útil (bueno, responde la mitad de mi pregunta, por lo que es medio útil ...). Hay muchas ocasiones en las que necesitas un presupuesto: Mike Boers da solo un gran ejemplo (de hecho, ese es el que me estoy encontrando) – YGA

+0

incluso peor, el ejemplo dado se rompe: (Pdb) list (shlex.shlex ("hello stack 'overflow' \ '' s 'really cool")) *** Error en el argumento:' (shlex.shlex ("hello stack \ 'overflow \' \\\ '\' s \ 'pretty cool "))' – YGA

25

Parece que

try: # py3 
    from shlex import quote 
except ImportError: # py2 
    from pipes import quote 

quote("hello stack overflow's quite cool") 
>>> '"hello stack overflow\'s quite cool"' 

me llega lo suficientemente lejos.

+1

Claro, 'subprocess' es muy bueno para comenzar procesos en python, pero para la generación de código, ¡esto era justo lo que necesitaba! +1 – SingleNegationElimination

+0

En Python 3, esto es 'shlex.quote'. – asmeurer

+2

En Windows, 'subprocess.list2cmdline' es más preciso. 'pipes.quote' siempre usa comillas simples, lo que no es aceptable en el entorno de línea de comandos de Windows. – Rockallite

4

estoy bastante seguro de que pipes.quote se rompe, y no debe ser utilizado, ya que no se ocupa de argumentos de longitud cero correctamente:

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

creo que el resultado debería ser algo así como

mycommand arg1 '' arg3 
+0

es suficiente. pero entonces necesitamos una mejor solución :-) – YGA

+1

'print 'mycommand% s'% ('' .join (quote (arg) or" '' "for arg in args))'? – Day

+4

Por iniciativa de John, [esto se arregló en Python 2.6.] (Http://bugs.python.org/issue7476) –

2

El subproceso de módulo de biblioteca estándar tiene la función list2cmdline que hace esto, aunque de acuerdo con Microsoft rules así que no estoy seguro de qué tan confiable funciona en entornos Unix para líneas de comando más complicadas.

3

Para fin de la cita, trate shlex.split()

5

para la cáscara citar, esto funciona: rigurosamente que he probado en POSIX. [Estoy asumiendo que la función list2cmdline suministrada por Python funciona como se anuncia en Windows]

# shell.py 
import os 
if os.name == 'nt': 
    from subprocess import list2cmdline 

    def quote(arg): 
     return list2cmdline([arg])[0] 
else: 
    import re 
    _quote_pos = re.compile('(?=[^-0-9a-zA-Z_./\n])') 

    def quote(arg): 
     r""" 
     >>> quote('\t') 
     '\\\t' 
     >>> quote('foo bar') 
     'foo\\ bar' 
     """ 
     # This is the logic emacs uses 
     if arg: 
      return _quote_pos.sub('\\\\', arg).replace('\n',"'\n'") 
     else: 
      return "''" 

    def list2cmdline(args): 
     return ' '.join([ quote(a) for a in args ]) 

Las pruebas son here, si a alguien le interesa.

+0

Ejemplo de contador: la cadena que se va a citar contiene '" \ xC3 \ xA9 "', que es una é en UTF-8, y por lo tanto no es poco común en los nombres de archivo. El código anterior pone barras invertidas delante de ambos caracteres, lo cual es incorrecto. 'pipes.quote' lo pondrá entre comillas simples. – greggo

0

La función quote está disponible desde hace bastante tiempo (Python 2.7?) - la principal desventaja es que se movió del módulo pipe a shlex entre 3.2 y 3.3.

tienes que estar preparado para manejar ambos casos, mientras que la importación de esa función:

try: 
    from shlex import quote 
except ImportError: 
    from pipes import quote