Ir al directorio usando variables bash no funciona cuando los nombres de directorio tienen espacios

11

Digamos que quiero almacenar el siguiente comando en una variable

cd "/cygdrive/c/Program Files/"

Entonces hago esto

dir="cd \"/cygdrive/c/Program Files/\""

Eso debería almacenar el comando para navegar al directorio Archivos de programa, por lo que cuando escribo $ dir me lleva a ese directorio. Para verificar que las citas se hayan escapado correctamente, escribo

echo $dir

lo que me da

cd "/cygdrive/c/Program Files/"

Entonces todo debería estar funcionando bien. Sin embargo, cuando escribo,

$dir

yo obtengo

bash: cd: "/cygdrive/c/Program: No such file or directory

¿Qué estoy haciendo mal? Estoy usando Cygwin, pero supongo que este problema se aplica a bash en general.

gsingh2011
fuente
Intente eliminar las comillas alrededor del nombre del directorio y, en su lugar, escape del espacio con una barra diagonal inversa.
Mike Fitzpatrick

Respuestas:

8

Respuesta corta: vea BashFAQ # 050 ("Estoy tratando de poner un comando en una variable, ¡pero los casos complejos siempre fallan!").

Respuesta larga: cuando bash analiza un comando, analiza las comillas antes de reemplazar las variables; nunca retrocede y analiza las comillas en los valores de las variables, por lo que terminan sin hacer nada útil. Usar echo para verificar el comando es completamente engañoso porque muestra el comando después de analizarlo; si desea ver lo que realmente se está ejecutando, use set -xlos comandos de impresión de shell a medida que se ejecutan, o use en printf "%q " $dir; echolugar de echo simple.

Si desea almacenar un comando complejo (es decir, uno con espacios u otros caracteres especiales en las "palabras"), debe colocarlo en una matriz en lugar de una variable de texto simple y luego expandirlo con el idioma `" $ { matriz [@]} ", así:

dir=(cd "/cygdrive/c/Program Files/")
"${dir[@]}"

Ahora, si el propósito es hacer una taquigrafía fácil de escribir, claramente este no es el camino a seguir. Use un alias o función de shell en su lugar:

alias dir='cd "/cygdrive/c/Program Files/"'
dir

o

dir() { cd "/cygdrive/c/Program Files/"; }
dir
Gordon Davisson
fuente
7

Cuando bash se expande $dir, solo divide palabras y se pega. La división de palabras produce tres palabras: cd, "/cygdrive/c/Programy Files/". Entonces el comando a ejecutar es cdcon dos argumentos; cdsolo mira su primer argumento "/cygdrive/c/Program, que no es un directorio existente.

Si desea realizar una evaluación completa del shell en el contenido de una variable, use eval:

eval "$dir"

Tenga en cuenta que necesita las comillas dobles $dir, de lo contrario, primero se realizaría la división de palabras, luego evalconcatenaría sus argumentos con espacios. Esto funcionaría aquí, pero en general iría mal (por ejemplo, si hubiera dos espacios consecutivos en un nombre de archivo).

Sin embargo, una cadena no es la forma correcta de almacenar un comando de shell que desea ejecutar. A menos que tenga un requisito inusual, debería usar una función en su lugar:

dir () {
  cd "/cygdrive/c/Program Files/"
}

Si usted está realmente interesado en la tipificación $dirde cambiar a un directorio en particular y esto no es sólo un ejemplo sencillo, puesto fiesta de 4, poner shopt -s autocden su .bashrcy conjunto

dir="/cygdrive/c/Program Files/"

después de lo cual puede escribir solo "/cygdrive/c/Program Files/"o "$dir"en el símbolo del sistema para cambiar a ese directorio. Todavía necesitas las comillas dobles $dir; si no te gusta, usa zsh en lugar de bash.

Gilles 'SO- deja de ser malvado'
fuente
6

Almacenar comandos en variables generalmente no es una buena idea. Para eso están las funciones y los alias.

Más bien que

dir="cd \"/cygdrive/c/Program Files/\""

prueba uno de estos:

dir="/cygdrive/c/Program Files"
...
cd "$dir"

o

dir() { cd "/cygdrive/c/Program Files"; }
dir

o

alias dir='cd "/tmp/Program Files"'
...
dir

La primera forma, almacenar el nombre del directorio en una variable, significa escribir un poco más, pero es más claro cuando ejecuta el comando que está haciendo a cd. El segundo es más cercano a lo que estás tratando de lograr. (No uso mucho los alias en bash; en realidad no estoy seguro de qué ventaja tienen sobre las funciones).

EDITAR:

Y dirprobablemente sea un mal nombre para un alias o función, ya que existe un comando con ese nombre (es básicamente una versión de ls, al menos si tiene coreutils de GNU).

Keith Thompson
fuente
upvote paraStoring commands in variables is generally not a good idea. That's what functions and aliases are for.
Rob