¿Cómo puedo desinfectar o escapar de rutas absolutas devueltas por realpath o readlink?


9

realpathy readlinkdevolver rutas absolutas:

+akiva@X230:~$ realpath ZannaIsAwesome
/home/akiva/ZannaIsAwesome

Un camino como ese es fácil de manejar. Sin embargo, algo como esto tendrá algunos problemas:

ingrese la descripción de la imagen aquí

Por ejemplo:

ingrese la descripción de la imagen aquí

Por lo tanto, un nombre como este debe desinfectarse para poder alimentarlo a otros comandos. Un caso de uso podría ser algo como esto:

+a@X230:~/\e[92mM@r|< $hu+'|'|_e|\|\|0rth [`-_-"]$ bacon=$(realpath pullingATerdon)
+a@X230:~$ vim $bacon 

No hace falta decir vim $baconque no funcionará como se esperaba.

¿Qué puedo hacer para desinfectar esa ruta absoluta para que funcione con otros comandos?


3
Siempre cite sus variables:vim "$bacon"
steeldriver

@steeldriver ¿Por qué es eso?
Akiva

3
Porque si no lo haces, te metes en situaciones como esta :)
terdon

Respuestas:


12

Cómo hacer esto correctamente

En primer lugar, siempre cita tus variables . Lo que intenta hacer funciona bien si lo cita correctamente:

$ pwd
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]
$ ls
pullingATerdon

He mantenido el extraño nombre de archivo que ha elegido ( aunque no tengo idea de por qué lo eligió ) en aras de la coherencia.

Ahora, asignemos la ruta de pullingATerdonuna variable y luego intentemos abrir el archivo:

$ bacon="$(realpath pullingATerdon)"
$ echo "$bacon"
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]/pullingATerdon
$ ls $bacon
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':

Eso falla, como se esperaba. Pero, si ahora lo citamos correctamente:

$ ls -l "$bacon"
-rw-r--r-- 1 terdon terdon 0 Mar 14 23:15 '/home/terdon/foo/\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]/pullingATerdon'

Funciona como se esperaba. Y sí, también puede abrir la ruta en un editor (adecuado): emacs "$bacon"funcionará bien. OK, también lo hará vimy cualquier otra cosa. Su elección de editor, aunque desafortunada, no es relevante.


¿Por qué falló el tuyo?

Una forma rápida de rastrear lo que realmente sucedió en su caso es usar set -x(apagarlo de nuevo con set +x), lo que hace que el shell imprima cada comando que ejecutará antes de ejecutarlo. encienda los mensajes de depuración del shell con set -x:

$ set -x
$ /bin/ls $bacon 
+ ls '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':

Esto nos muestra que lsse ejecutan con tres argumentos distintos: '/home/terdon/foo/\e[92mM@r|<', '+'\''|'\''|_e|\|\|0rth'y '[`-_-"]/pullingATerdon'. Esto sucede porque el shell realiza la división de palabras y la expansión global en cadenas sin comillas. En este caso, el problema es la división de palabras, ya que el shell vio los espacios en la ruta y leyó cada cadena separada por espacios como un argumento separado.

El mkdirejemplo es ligeramente diferente, pero es porque nos está mostrando el mensaje de error de la segunda invocación del comando. Supongo que lo probaste una vez y luego lo ejecutaste por segunda vez para obtener el resultado de tu pregunta. La primera vez que lo ejecutó, se vería así:

$ mkdir $(realpath pullingATerdon)
++ realpath pullingATerdon
+ mkdir '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
mkdir: cannot create directory ‘[`-_-"]/pullingATerdon’: No such file or directory

Nuevamente, eso intentará crear tres directorios, no uno, debido a la división de palabras. Primero, creó (con éxito) el directorio /home/terdon/foo/\e[92mM@r|<:

$ ls -l /home/terdon/foo/
total 8
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|<'
drwxr-xr-x 3 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]'

Luego, también con éxito, creó un directorio llamado +'|'|_e|\|\|0rthen su directorio actual:

$ ls -l
total 4
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:37 '+'\''|'\''|_e|\|\|0rth'
-rw-r--r-- 1 terdon terdon    0 Mar 15 00:36  pullingATerdon

Y luego, intentó crear el directorio [`-_-"]/pullingATerdon. Esto falló porque mkdir, de manera predeterminada, no crea subdirectorios (si lo ejecuta, puede hacerlo -p):

$ mkdir baz/bar
mkdir: cannot create directory baz/bar’: No such file or directory

Como su cadena sin comillas contenía un /, mkdirconsideró que una ruta de dos directorios, trató de encontrar el superior, y falló.

Por eso falló, pero lo que sucedió es más complicado. La cadena ha utilizado es en realidad un pegote cáscara, específicamente un rango pegote , que coincide con todos los archivos en el directorio actual y cuyo nombre es uno de los 5 caracteres `, -, _o ". Como no tiene tales archivos en su directorio actual, el glob no coincide con nada y, como es el comportamiento predeterminado en bash, se devuelve a sí mismo:

$ echo "[\`-_-\"]/pullingATerdon"  ## some escaping is needed here
+ echo '[`-_-"]/pullingATerdon'    ## but it echoes the right thing
[`-_-"]/pullingATerdon             ## and matches nothing, so returns itself.

Para aclarar, esto es lo que sucede si le das un globo que coincide con algo:

$ echo [p]*   ## any filename starting with a p
pullingATerdon
$ echo "[p]*" ## the string "[p]*"
[p]*

Lo no entrecomillado [p*]se expande a la lista de nombres de archivos coincidentes (solo uno, en este caso) y eso es a lo que se pasa echo. Otra razón más por la que deberías citar todas las cosas.

Finalmente, el error real que muestra es de la segunda vez que ejecutó el comando y falla en el primer paso, al intentar crear /home/terdon/foo/\e[92mM@r|<, porque la invocación anterior ya había creado ese directorio.


En términos más generales, cada vez que se encuentre trabajando con nombres de archivos arbitrarios, use siempre globos de shell. Cosas como esta:

for file in *; do command "$file"; done

Eso funcionará para cualquier nombre de archivo. No importa lo que contenga. En nuestro ejemplo anterior, podrías haber hecho:

emacs /home/terdon/*92mM*/pullingATerdon

Cualquier globo que identifique el archivo de destino lo hará de manera única. De esa manera, no necesita preocuparse por los caracteres especiales y simplemente puede dejar que el shell los maneje.


Algunas referencias útiles:

  1. ¿Cómo puedo encontrar y manejar de forma segura los nombres de archivo que contienen nuevas líneas, espacios o ambos? : Una de las preguntas frecuentes en el excelente Wiki de Gray Cat.

  2. Implicaciones de seguridad de olvidarse de citar una variable en shells bash / POSIX : la misma publicación a la que hice referencia al comienzo de esta respuesta. Una explicación excelente y muy detallada de todas las cosas que podrían salir mal si no cita las variables de shell correctamente.

  3. ¿Por qué mi script de shell se ahoga en espacios en blanco u otros caracteres especiales? : todo lo que siempre quiso saber sobre el manejo de nombres de archivos arbitrarios en el shell.

  4. ¿Cuándo es necesaria la doble cita? : Más información sobre citas y variables y, específicamente, los pocos casos en los que no necesita citarlas

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.