2010-06-28 18 views
5

En una secuencia de comandos de BASH, intento detectar si existe un archivo. El nombre del archivo está en una variable pero el comando -e parece no poder detectar el archivo. El siguiente código siempre imprime "~/misc/tareas/drupal_backup.sh no existe"Script de Bash: no se detecta el nombre de archivo en una variable

filename="~/misc/tasks/drupal_backup.sh" 

if [ -e "$filename" ]; then 
    echo "$filename exists" 
else 
    echo "$filename does not exist" 
fi 

Por otro lado, el siguiente código detecta el archivo correctamente:

if [ -e ~/misc/tasks/drupal_backup.sh ]; then 
    echo "$filename exists" 
else 
    echo "$filename does not exist" 
fi 

¿Por qué sería ? ¿Cómo puedo detectar el archivo cuando el nombre del archivo está en una variable?

+1

Considero que el '~' es una conveniencia de la línea de comandos que no se debe usar en las secuencias de comandos. –

Respuesta

7

Eso es interesante. Sustituir $HOME por ~ funciona igual que eliminando las comillas de la asignación.

Si se pone un set -x en la parte superior de esa secuencia de comandos, verá que la versión con citas establece el nombre de archivo a ~/... que es lo que está determinado a -e. Si elimina las comillas, el nombre de archivo se establece en /home/somebody/... expandido. Entonces, en el primer caso, verá:

y no le gusta. En el segundo caso, se ve:

+ [ -e /home/somebody/... ] 

y lo hace trabajo.

Si lo hace sin la variable, que se ve:

+ [ -e /home/somebody/... ] 

y, sí, funciona.


Después de un poco de investigación, me he dado cuenta que en realidad es el orden en que bash realiza sus expansiones. Desde la página de manual de bash:

El orden de las expansiones es: expansión de llaves, de tilde, parámetros, variables y la expansión y dominio aritmética sustitución (hecho de una forma de izquierda a derecha), la división de palabras, y de la ruta expansión.

Es por eso que no está funcionando, la variable se sustituye después de la expansión de tilde. En otras palabras, en el punto donde bash quiere expandir ~, no hay ninguna. Solo después de la expansión de variables cambia la palabra a ~/... y no habrá expansión de tilde después de eso.

Una cosa que pudiera hacer es cambiar su estado de cuenta a if:

if [[ -e $(eval echo $filename) ]]; then 

Este evaluará el argumento de nombre de archivo $ dos veces. La primera vez (con eval), no habrá ~ durante la fase de expansión de tilde, pero $filename se cambiará a ~/... durante la fase de expansión variable.

Luego, en la segunda evaluación (la que se hace como parte del propio if), el ~ estará allí durante la fase de expansión de tilde.

Lo he probado en mi archivo .profile y parece funcionar, le sugiero que confirme en su caso particular.

+0

¡Gracias! Simplemente reemplazando el ~ con $ HOME funcionó perfectamente. – thornate

+1

Debe tenerse en cuenta que nunca debe usar 'eval'. '~' es una buena abreviatura para el modo interactivo, pero debe ser avioded en scripts por exactamente esta razón. Muchos guiones de inicio contienen algo como '[-r ~/.profile] &&. ~/.profile', y no sé si esto funciona si el directorio de inicio contiene espacios. Mejor es usar $ HOME y citar todas las variables: 'filename =" $ HOME/misc/tasks/drupal_backup.sh "'. En este caso, no es necesario el presupuesto, pero no es dañino y es más fácil simplemente citar todo a menos que sepa exactamente dónde puede omitir las comillas. – Philipp

+0

@Phillipp, si va a hacer una afirmación general como "nunca debe usar eval" en una meritocracia, puede pensar en respaldarlo con su razonamiento :-) – paxdiablo

Cuestiones relacionadas