realpath
y readlink
devolver 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:
Por ejemplo:
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 $bacon
que no funcionará como se esperaba.
¿Qué puedo hacer para desinfectar esa ruta absoluta para que funcione con otros comandos?
command-line
bash
paths
Akiva
fuente
fuente
vim "$bacon"
Respuestas:
Cómo hacer esto correctamente
En primer lugar, siempre cita tus variables . Lo que intenta hacer funciona bien si lo cita correctamente:
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
pullingATerdon
una variable y luego intentemos abrir el archivo:Eso falla, como se esperaba. Pero, si ahora lo citamos correctamente:
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ávim
y 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 conset +x
), lo que hace que el shell imprima cada comando que ejecutará antes de ejecutarlo. encienda los mensajes de depuración del shell conset -x
:Esto nos muestra que
ls
se 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
mkdir
ejemplo 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í: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|<
:Luego, también con éxito, creó un directorio llamado
+'|'|_e|\|\|0rth
en su directorio actual:Y luego, intentó crear el directorio
[`-_-"]/pullingATerdon
. Esto falló porquemkdir
, de manera predeterminada, no crea subdirectorios (si lo ejecuta, puede hacerlo-p
):Como su cadena sin comillas contenía un
/
,mkdir
consideró 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:Para aclarar, esto es lo que sucede si le das un globo que coincide con algo:
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 pasaecho
. 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:
Eso funcionará para cualquier nombre de archivo. No importa lo que contenga. En nuestro ejemplo anterior, podrías haber hecho:
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:
¿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.
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.
¿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.
¿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
fuente