11

Let's say I want to store the following command in a variable

cd "/cygdrive/c/Program Files/"

So I do this

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

That should store the command to navigate to the Program Files directory, so when I type $dir it takes me to that directory. To check that the quotations have been properly escaped, I type

echo $dir

which gives me

cd "/cygdrive/c/Program Files/"

So everything should be working fine. However, when I type,

$dir

I get

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

What am I doing wrong? I'm using Cygwin, but I assume this problem applies to bash in general.

1
  • Try removing the quotes around the directory name and escape the space with a backslash instead. Nov 19, 2011 at 21:01

4 Answers 4

8

Short answer: see BashFAQ #050 ("I'm trying to put a command in a variable, but the complex cases always fail!").

Long answer: when bash parses a command, it parses quotes before it replaces variables; it never goes back and re-parses quotes in the values of variables, so they wind up not doing anything useful. Using echo to check the command is completely misleading because it shows the command after it's been parsed; if you want to see what's really being executed use either set -x to have the shell print commands as they're executed, or use printf "%q " $dir; echo instead of plain echo.

If you want to store a complex command (i.e. one with spaces or other special characters in the "words"), you need to put it in an array instead of a simple text variable, and then expand it with the idiom `"${array[@]}", like this:

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

Now, if the purpose is to make an easy-to-type shorthand, this is clearly not the way to go. Use a shell alias or function instead:

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

or

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

When bash expands $dir, it only performs word splitting and globbing on it. Word splitting produces three words: cd, "/cygdrive/c/Program and Files/". Then the command to execute is cd with two arguments; cd only looks at its first argument, "/cygdrive/c/Program, which is not an existing directory.

If you want to perform full shell evaluation on the content of a variable, use eval:

eval "$dir"

Note that you need the double quotes around $dir, otherwise word splitting would first be performed, then eval would concatenate its arguments with spaces. This would happen to work here, but would go bad in general (e.g. if there were two consecutive spaces in a file name).

However, a string is not the right way to store a shell command that you want to execute. Unless you have an unusual requirement, you should be using a function instead:

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

If you are really interested in typing $dir to switch to a particular directory and this isn't just a simple example, since bash 4, put shopt -s autocd in your .bashrc and set

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

after which you can type just "/cygdrive/c/Program Files/" or "$dir" at the shell prompt to switch to that directory. You still need the double quotes around $dir; if you don't like that, use zsh instead of bash.

6

Storing commands in variables is generally not a good idea. That's what functions and aliases are for.

Rather than

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

try one of these:

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

or

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

or

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

The first form, storing the name of the directory in a variable, means a little more typing, but it's clearer when you execute the command that you're doing a cd. The second is closest to what you're trying to accomplish. (I don't use aliases much in bash; I'm not actually sure what advantage they have over functions.)

EDIT :

And dir is probably a bad name for an alias or function, since there's an existing command by that name (it's basically a version of ls, at least if you have GNU coreutils).

1
  • upvote for Storing commands in variables is generally not a good idea. That's what functions and aliases are for.
    – Rob
    Nov 19, 2011 at 22:02
-1

Try

bash$ echo  'This   is   "a     long"  line' > mylist
bash$ echo @mylist
@mylist
bash$ cmd
c:\> c:\cygwin\bin\echo @mylist
This is a     long line

But even more specifically:

https://stackoverflow.com/questions/4334467/cygwin-using-a-path-variable-containing-a-windows-path-with-a-space-in-it

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .