shlex — Análisis léxico simple

Código fuente: Lib/shlex.py


La clase shlex facilita la escritura de analizadores léxicos para sintaxis simples que se parecen al intérprete de comandos de Unix. Esto será a menudo útil para escribir pequeños lenguajes, (por ejemplo, en archivos de control para aplicaciones Python) o para analizar cadenas de texto citadas.

El módulo shlex define las siguientes funciones:

shlex.split(s, comments=False, posix=True)

Divide la cadena de caracteres s usando una sintaxis similar a la de un intérprete de comandos. Si comments es False (por defecto), el análisis de los comentarios en la cadena de caracteres dada sera deshabilitada (estableciendo el atributo commenters de la instancia shlex a la cadena de caracteres vacía). Esta función trabaja en modo POSIX por defecto, pero utiliza el modo non-POSIX si el argumento posix es falso.

Nota

Como la función split() inicia una instancia shlex, al pasar None por s se leerá la cadena de caracteres para separarse de la entrada estándar.

Obsoleto desde la versión 3.9: Passing None for s will raise an exception in future Python versions.

shlex.join(split_command)

Concatena los tokens de la lista split_command y retorna una cadena. Esta función es la inversa de split().

>>> from shlex import join
>>> print(join(['echo', '-n', 'Multiple words']))
echo -n 'Multiple words'

El valor retornado se escapa del intérprete de comandos para protegerlo contra vulnerabilidades de inyección (consulte quote()).

Nuevo en la versión 3.8.

shlex.quote(s)

Retorna una versión con escape del intérprete de comandos de la cadena s. El valor retornado es una cadena que se puede usar de forma segura como un token en un intérprete de línea de comandos, para los casos en los que no se puede usar una lista.

Este idioma sería inseguro:

>>> filename = 'somefile; rm -rf ~'
>>> command = 'ls -l {}'.format(filename)
>>> print(command)  # executed by a shell: boom!
ls -l somefile; rm -rf ~

quote() te permite tapar el agujero de seguridad:

>>> from shlex import quote
>>> command = 'ls -l {}'.format(quote(filename))
>>> print(command)
ls -l 'somefile; rm -rf ~'
>>> remote_command = 'ssh home {}'.format(quote(command))
>>> print(remote_command)
ssh home 'ls -l '"'"'somefile; rm -rf ~'"'"''

La cita es compatible con los intérpretes de comandos UNIX y con split():

>>> from shlex import split
>>> remote_command = split(remote_command)
>>> remote_command
['ssh', 'home', "ls -l 'somefile; rm -rf ~'"]
>>> command = split(remote_command[-1])
>>> command
['ls', '-l', 'somefile; rm -rf ~']

Nuevo en la versión 3.3.

El módulo shlex define las siguientes clases:

class shlex.shlex(instream=None, infile=None, posix=False, punctuation_chars=False)

Una instancia o instancia de la subclase shlex es un objeto de analizador léxico. El argumento de inicialización, si está presente, especifica de dónde leer caracteres. Debe ser un objeto similar a un archivo/stream con los métodos read() y readline() o una cadena de caracteres. Si no se da ningún argumento, la entrada se tomará de sys.stdin. El segundo argumento opcional es una cadena de nombre de archivo, que establece el valor inicial del atributo infile. Si el argumento instream es omitido o es igual a sys.stdin, este segundo argumento tiene como valor predeterminado «stdin». El argumento posix define el modo operativo: cuando posix no es true (predeterminado), la instancia shlex funcionará en modo de compatibilidad. Cuando se opera en modo POSIX, shlex intentará estar lo más cerca posible de las reglas de análisis de el intérprete de comandos POSIX. El argumento punctuation_chars proporciona una manera de hacer que el comportamiento sea aún más cercano a la forma en que se analizan los intérpretes de comandos reales. Esto puede tomar una serie de valores: el valor predeterminado, False, conserva el comportamiento visto en Python 3.5 y versiones anteriores. Si se establece en True, se cambia el análisis de los caracteres ();<>|&: cualquier ejecución de estos caracteres (caracteres considerados de puntuación) se retorna como un único token. Si se establece en una cadena de caracteres no vacía, esos caracteres se utilizarán como caracteres de puntuación. Los caracteres del atributo wordchars que aparecen en punctuation_chars se eliminarán de wordchars. Consulte Compatibilidad mejorada con intérprete de comandos para obtener más información. punctuation_chars solo se puede establecer en la creación de la instancia shlex y no se puede modificar más adelante.

Distinto en la versión 3.6: Se añadió el parámetro punctuation_chars.

Ver también

Módulo configparser

Analizador de archivos de configuración similares a los archivos .ini de Windows.

objetos shlex

Una instancia shlex tiene los siguientes métodos:

shlex.get_token()

Retorna un token. Si los tokens han sido apiladas usando push_token(), saca una ficha de la pila. De lo contrario, lee uno de la secuencia de entrada. Si la lectura encuentra un fin de archivo inmediato, eof se retorna (la cadena de caracteres vacía ('') en modo no-POSIX, y None en modo POSIX).

shlex.push_token(str)

Coloca el argumento en la pila de tokens.

shlex.read_token()

Lee un token sin procesar. Ignora la pila de retroceso y no interpreta las peticiones de la fuente. (Este no es normalmente un punto de entrada útil, y está documentado aquí sólo para completarlo.)

shlex.sourcehook(filename)

Cuando shlex detecta una petición de fuente (ver source abajo) este método recibe el siguiente token como argumento, y se espera que retorne una tupla que consista en un nombre de archivo y un archivo abierto como objeto.

Normalmente, este método primero elimina las comillas del argumento. Si el resultado es un nombre de ruta absoluto, o no había ninguna solicitud de origen anterior en vigor, o el origen anterior era una secuencia (como sys.stdin), el resultado se deja solo. De lo contrario, si el resultado es un nombre de ruta relativo, la parte del directorio del nombre del archivo se pone inmediatamente antes en la pila de inclusión de origen (este comportamiento es similar a la forma en que el preprocesador de C controla #include "file.h").

El resultado de las manipulaciones se trata como un nombre de archivo y se retorna como el primer componente de la tupla, con open() llamado en él para producir el segundo componente. (Nota: esto es lo contrario del orden de los argumentos en la inicialización de instancia!)

Este enlace se expone para que pueda usarlo para implementar rutas de búsqueda de directorios, adición de extensiones de archivo y otros trucos de espacios de nombres. No hay ningún enlace “close” correspondiente, pero una instancia de shlex llamará el método close() de la secuencia de entrada de origen cuando retorna EOF.

Para un control más explícito del apilamiento de código fuente, utilice los métodos push_source() y pop_source().

shlex.push_source(newstream, newfile=None)

Inserta una secuencia de fuente de entrada en la pila de entrada. Si se especifica el argumento de nombre de archivo, más adelante estará disponible para su uso en mensajes de error. Este es el mismo método utilizado internamente por el método sourcehook().

shlex.pop_source()

Saca la última fuente de entrada de la pila de entrada. Este es el mismo método que el analizador léxico utiliza cuando llega al EOF en una secuencia de entrada apilada.

shlex.error_leader(infile=None, lineno=None)

Este método genera un mensaje de error líder en el formato de una etiqueta de error del compilador de Unix C; el formato es '"%s", line %d: ', donde el %s es reemplazado con el nombre del archivo fuente actual y el %d con el número de línea de entrada actual (los argumentos opcionales pueden ser usado para sobrescribir estos).

Esta conveniencia se proporciona para animar a los usuarios de shlex a generar mensajes de error en el formato estándar y analizable que entienden Emacs y otras herramientas de Unix.

Las instancias de las subclases shlex tienen algunas variables de instancia pública que controlan el análisis léxico o pueden ser usadas para la depuración:

shlex.commenters

La cadena de caracteres que son reconocidos como comentarios de principiantes. Todos los caracteres desde el comentario principiante hasta el final de la línea son ignorados. Incluye sólo '#' por defecto.

shlex.wordchars

La cadena de caracteres que se acumulará en tokens de varios caracteres. Por defecto, incluye todos los alfanuméricos ASCII y el subrayado. En el modo POSIX, los caracteres acentuados del conjunto Latin-1 también están incluidos. Si punctuation_chars no está vacío, los caracteres ~-./*?=, que pueden aparecer en las especificaciones del nombre de archivo y en los parámetros de la línea de comandos, también se incluirán en este atributo, y cualquier carácter que aparezca en punctuation_chars será eliminado de wordchars si están presentes allí. Si whitespace_split se establece en True, esto no tendrá ningún efecto.

shlex.whitespace

Caracteres que serán considerados como espacio en blanco y salteados. El espacio blanco limita los tokens. Por defecto, incluye espacio, tabulación, salto de línea y retorno de carro.

shlex.escape

Caracteres que serán considerados como de escape. Esto sólo se usará en el modo POSIX, e incluye sólo '\' por defecto.

shlex.quotes

Los caracteres que serán considerados como citas de la cadena. El token se acumula hasta que la misma cita se encuentra de nuevo (así, los diferentes tipos de citas se protegen entre sí como en el intérprete de comandos.) Por defecto, incluye ASCII comillas simples y dobles.

shlex.escapedquotes

Caracteres en quotes que interpretarán los caracteres de escape definidos en escape. Esto sólo se usa en el modo POSIX, e incluye sólo '"' por defecto.

shlex.whitespace_split

Si es True, los tokens sólo se dividirán en espacios en blanco. Esto es útil, por ejemplo, para analizar las líneas de comando con shlex, obteniendo los tokens de forma similar a los argumentos de el intérprete de comandos. Cuando se utiliza en combinación con puntuation_chars, los tokens se dividirán en espacios en blanco además de esos caracteres.

Distinto en la versión 3.8: El atributo puntuation_chars se hizo compatible con el atributo whitespace_split.

shlex.infile

El nombre del archivo de entrada actual, como se estableció inicialmente al momento de la instanciación de la clase o apilado por solicitudes de fuente posteriores. Puede ser útil examinar esto cuando se construyan mensajes de error.

shlex.instream

El flujo de entrada del cual esta instancia shlex está leyendo caracteres.

shlex.source

Este atributo es None por defecto. Si le asignas una cadena, esa cadena será reconocida como una solicitud de inclusión a nivel léxico similar a la palabra clave source en varios intérpretes de comandos. Es decir, el token inmediatamente siguiente se abrirá como un nombre de archivo y la entrada se tomará de ese flujo hasta EOF, en cuyo momento se llamará al método close() de ese flujo y la fuente de entrada se convertirá de nuevo en el flujo de entrada original. Las peticiones de origen pueden ser apiladas a cualquier número de niveles de profundidad.

shlex.debug

Si este atributo es numérico y 1 o más, una instancia shlex imprimirá una salida de progreso verboso en su comportamiento. Si necesitas usar esto, puedes leer el código fuente del módulo para conocer los detalles.

shlex.lineno

Numero de linea de fuente (conteo de las nuevas lineas vistas hasta ahora mas uno).

shlex.token

El buffer de tokens. Puede ser útil examinarlo cuando se capturan excepciones.

shlex.eof

Token usado para determinar el final del archivo. Esto se ajustará a la cadena de caracteres vacía (''), en el modo no-POSIX, y a None en el modo POSIX.

shlex.punctuation_chars

Una propiedad de sólo lectura. Caracteres que serán considerados como puntuación. Las series de caracteres de puntuación se retornarán como un único token. Sin embargo, tenga en cuenta que no se realizará ninguna comprobación de validez semántica: por ejemplo, “>>>” podría ser retornado como un token, aunque no sea reconocido como tal por los intérpretes de comandos.

Nuevo en la versión 3.6.

Reglas de análisis

Cuando se opera en modo no-POSIX, shlex intentará obedecer las siguientes reglas.

  • Los caracteres entre comillas no son reconocidos dentro de las palabras (Do"Not"Separate es analizado como la única palabra Do"Not"Separate);

  • Los caracteres de escape no son reconocidos;

  • El encerrar los caracteres entre comillas preserva el valor literal de todos los caracteres dentro de las comillas;

  • Las comillas finales separan las palabras ("Do"Separate es analizado como "Do" y Separate);

  • Si whitepace_split es False, cualquier carácter que no sea declarado como un carácter de palabra, espacio en blanco, o una cita será retornado como un token de un solo carácter. Si es True, shlex sólo dividirá las palabras en espacios en blanco;

  • EOF es señalado con una cadena de caracteres vacía ('');

  • No es posible analizar cadenas de caracteres vacías, incluso si se citan.

Cuando se opera en el modo POSIX, shlex intentará obedecer a las siguientes reglas de análisis.

  • Las comillas se eliminan y no separan las palabras ("Do"Not"Separate" se analiza como la sola palabra DoNotSeparate);

  • Los caracteres de escape no citados (por ejemplo '\') conservan el valor literal del siguiente carácter que continua;

  • Encerrar caracteres entre comillas que no forman parte de escapedquotes (por ejemplo, "") conservan el valor literal de todos los caracteres dentro de las comillas;

  • Encerrar caracteres entre comillas que forman parte de escapedquotes (por ejemplo, "") conserva el valor literal de todos los caracteres dentro de las comillas, con la excepción de los caracteres mencionados en escape. Los caracteres de escape conservan su significado especial solo cuando van seguidos de la comilla en uso, o el propio carácter de escape. De lo contrario, el carácter de escape sera considerado un carácter normal.

  • EOF es señalado con un valor None;

  • Se permiten cadenas de caracteres vacías entrecomilladas ('').

Compatibilidad mejorada con intérprete de comandos

Nuevo en la versión 3.6.

La clase shlex provee compatibilidad con el análisis realizado por los intérprete de comandos comunes de Unix como bash, dash y sh. Para aprovechar esta compatibilidad, especifica el argumento punctuation_chars en el constructor. Esto por defecto es False, el cual conserva el comportamiento pre-3.6. Sin embargo, si se establece como True, entonces se cambia el análisis de los caracteres ();<>|&: cualquier ejecución de estos caracteres se retorna como un único token. Mientras que esto se queda corto en un analizador completo para los intérprete de comandos (que estaría fuera del alcance de la biblioteca estándar, dada la multiplicidad de intérpretes de comandos que hay), te permite realizar el procesamiento de las líneas de comandos más fácilmente de lo que podrías hacerlo de otra manera. Para ilustrarlo, puede ver la diferencia en el siguiente fragmento:

 >>> import shlex
 >>> text = "a && b; c && d || e; f >'abc'; (def \"ghi\")"
 >>> s = shlex.shlex(text, posix=True)
 >>> s.whitespace_split = True
 >>> list(s)
 ['a', '&&', 'b;', 'c', '&&', 'd', '||', 'e;', 'f', '>abc;', '(def', 'ghi)']
 >>> s = shlex.shlex(text, posix=True, punctuation_chars=True)
 >>> s.whitespace_split = True
 >>> list(s)
 ['a', '&&', 'b', ';', 'c', '&&', 'd', '||', 'e', ';', 'f', '>', 'abc', ';',
 '(', 'def', 'ghi', ')']

Por supuesto, se retornarán tokens que no son válidos para los intérpretes de comandos y deberá implementar sus propias comprobaciones de errores en los tokens retornados.

En lugar de pasar True como valor para el parámetro punctuation_chars, puede pasar una cadena con caracteres específicos, que se usará para determinar qué caracteres constituyen puntuación. Por ejemplo:

>>> import shlex
>>> s = shlex.shlex("a && b || c", punctuation_chars="|")
>>> list(s)
['a', '&', '&', 'b', '||', 'c']

Nota

Cuando es especificado punctuation_chars, el atributo wordchars se aumenta con los caracteres ~-./*?=. Esto se debe a que estos caracteres pueden aparecer en los nombres de archivo (incluidos los comodines) y en los argumentos de la línea de comandos (por ejemplo, --color=auto). Por lo tanto:

>>> import shlex
>>> s = shlex.shlex('~/a && b-c --color=auto || d *.py?',
...                 punctuation_chars=True)
>>> list(s)
['~/a', '&&', 'b-c', '--color=auto', '||', 'd', '*.py?']

Sin embargo, para que coincida con el intérprete de comandos lo más cerca posible, se recomienda utilizar siempre posix y whitespace_split cuando se utiliza punctuation_chars, el cual negará por completo wordchars.

Para obtener el mejor efecto, punctuation_chars debe establecerse junto con posix=True. (Tenga en cuenta que posix=False es el valor predeterminado para shlex.)