Informática geek, matemáticas, pensamiento crítico y alguna otra cosa de vez en cuando.

2009-12-04

Escape del escape del escape

Reproduzco aquí esta entrada que en su día fue publicada en Uno por uno, uno...


Existe en muchos sistemas informáticos un medio para escribir caracteres que dentro del contexto en el que se escriben tendrían otro significado. Por ejemplo, en HTML se usa el símbolo & para introducir caracteres o comandos especiales; en las URLs el símbolo usado es el %, en MIME es el signo =, en el prompt de MSDOS era el signo dólar, y el lenguaje C y muchos otros derivados usan la barra invertida (\). Nada que objetar; es un excelente medio para conseguir incluir caracteres que podemos querer usar y la sintaxis del sistema en cuestión nos lo impediría sin ellos. Estos caracteres se suelen llamar «escapes», ya que nos permiten «escapar» de las limitaciones de dicho sistema.

De hecho, en casi todos los casos el propio carácter especial necesita una forma de ser expresado mediante sí mismo: el & se escribe & en HTML, el % se escribe %25 en una URL y el = se escribe =3D en MIME. Algunos han tenido una «feliz» idea: cuando se escribe dos veces el carácter de escape, significa el propio carácter de escape. Así, en el prompt de MSDOS se escribiría $$ para significar un solo símbolo dólar, y en C y similares, se usa \\ para indicar una sola barra invertida.

Pobres de nosotros, quienes tuvieron esa última ocurrencia de duplicar el carácter, no pensaron en las consecuencias. Valga como ejemplo el hecho de que el C, el sh, las expresiones regulares y el PHP emplean todos el mismo convenio de carácter especial (\) y duplicación para obtenerlo (\\).

Supongamos este escenario: tenemos que escribir en PHP una página web para un lugar que aloja archivos fuente en C, que admita que sus usuarios busquen cadenas de texto en dichos archivos con expresiones regulares. Por fastidiar, el PHP donde se va a alojar no tiene compilada la librería de expresiones regulares, así que la solución pasa por invocar el comando 'grep' de la línea de comandos. Algunas búsquedas comunes irán ya codificadas de antemano dentro del código PHP. ¿Cómo habría que escribir en PHP la cadena que buscara, en el código fuente en C, una ruta de un archivo como C:\Dir\Arch.txt?

En el archivo en C, esas cadenas vendrían escritas como "C:\\Dir\\Arch.txt", debido a la duplicación del carácter de escape. Así que eso es lo que tiene que buscar el grep. Pero, como las expresiones regulares también utilizan el mismo carácter de escape, en realidad la línea de comandos del grep debería parecerse a: grep 'C:\\\\Dir\\\\Arch.txt' *.c

Sin embargo, existe alguna incompatibilidad en los caracteres de escape dependiendo del shell (el ash también requiere que se duplique la barra invertida en las cadenas entre apóstrofes, a diferencia del bash; sólo las cadenas entre comillas son compatibles entre sí). De modo que, para asegurar que funcionará tanto en ash como en bash, en lugar de eso habría que prescindir de los apóstrofes y emplear comillas, teniendo que escribir: grep "C:\\\\\\\\Dir\\\\\\\\Arch.txt" *.c

¿Y qué cadena fija escribiríamos en nuestro fuente en PHP?

$cadena = 'grep "C:\\\\\\\\\\\\\\\\Dir\\\\\\\\\\\\\\\\Arch.txt" *.c';

Si todos los sistemas hubieran tenido el convenio de que para generar una barra invertida bastara con un carácter que NO es una barra invertida, la cosa sería mucho más sencilla. Por ejemplo, supongamos que la secuencia \k representara una barra invertida en todos los sistemas implicados en nuestro ejemplo. Entonces, el código fuente en C contendría algo como: "C:\kDir\kArch.txt", por lo que la línea de comandos del grep sería: grep "C:\kkkDir\kkkArch.txt" *.c, y nuestra cadena fija: $cadena = 'grep "C:\kkkkDir\kkkkArch.txt" *.c', lo cual únicamente significa agregar una k en cada nivel, mucho más inocente que la duplicación. Porque, imagínense, si esa cadena tuviera que ir escrita en algo como un generador para esos archivos PHP, quedaría así:

'$cadena = \'grep "C:\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Dir\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Arch.txt" *.c\';'

Así que, por favor, si alguna vez necesita escoger un carácter de escape, piénselo dos veces antes de elegir la barra invertida, y tres veces antes de decidir que la duplicación del carácter es una referencia a sí mismo.

No comments: