21

Say I have a folder called Foo located in /home/user/ (my /home/user also being represented by ~).

I want to have a variable

a="~/Foo" and then do

cd $a

I get -bash: cd: ~/Foo: No such file or directory

However if I just do cd ~/Foo it works fine. Any clue on how to get this to work?

codeforester
  • 28,846
  • 11
  • 78
  • 104
Benjamin
  • 2,198
  • 5
  • 18
  • 19
  • 2
    Of potential interest to people here: [How to manually expand a special variable (ex. tilde) in bash](https://stackoverflow.com/questions/3963716/how-to-manually-expand-a-special-variable-ex-tilde-in-bash). I strongly suggest that you **DO NOT** use any answer, either in this question or elsewhere, which directs you to use `eval`; see [BashFAQ #48](http://mywiki.wooledge.org/BashFAQ/048). – Charles Duffy Jun 29 '18 at 20:58

5 Answers5

28

You can do (without quotes during variable assignment):

a=~/Foo
cd "$a"

But in this case the variable $a will not store ~/Foo but the expanded form /home/user/Foo. Or you could use eval:

a="~/Foo"
eval cd "$a"
bmk
  • 13,031
  • 5
  • 34
  • 40
  • Thank you! The eval cd worked. I indeed wanted to keep the "~/Foo" intact, so this solution works. At first I tried cd `eval $a` but that didnt work. – Benjamin Apr 21 '11 at 18:38
  • 6
    The usual warnings against using `eval` should be flagged prominently here as well. **Never** run `eval` on data you have not sanitized, or constructed yourself in a safe manner. – tripleee Nov 19 '14 at 23:27
  • 2
    Unfortunately, this won't work if the directory name contains spaces. `a="~/dir with spaces"; eval cd "$a"` => `-bash: cd: /Users/jack/dir: No such file or directory` :-( – duthen Aug 17 '17 at 12:45
  • @duthen: You could use the somwhat ugly variant `cd "$(eval echo "$a")"` – bmk Nov 20 '17 at 13:42
15

You can use $HOME instead of the tilde (the tilde is expanded by the shell to the contents of $HOME). Example:

dir="$HOME/Foo";
cd "$dir";
user268396
  • 10,338
  • 26
  • 25
  • 1
    What's the benefit of the quotes, if your name isn't "John F.", and why the semicolons? – user unknown Apr 21 '11 at 18:48
  • Thank you for mentioning that tilde actually expands to $HOME, which in my opinion is more readable anyway. – DusteD Nov 29 '17 at 08:04
  • 2
    @userunknown, if you leave the quotes out, you're telling the shell to *do more things*, and those things all have failure cases. If someone set `IFS=/` to read a slash-delimited input, for example, `cd $dir` will misbehave even with no spaces present. Expanding strings unquoted tells the shell to string-split and glob-expand them. Why would you write code that directs the shell to do things you don't ever want it to do? – Charles Duffy Jun 29 '18 at 20:55
0

A much more robust solution would be to use something like sed or even better, bash parameter expansion:

somedir="~/Foo/test~/ing";
cd ${somedir/#\~/$HOME}

or if you must use sed,

cd $(echo $somedir | sed "s#^~#$HOME#")
khamer
  • 139
  • 1
  • 3
  • Unfortunately, this won't work if you try to refer to someone else's home directory. `somedir="~jack/TMP"; cd ${somedir/#\~/$HOME}` => `-bash: cd: /Users/jackjack/TMP: No such file or directory` :-( – duthen Aug 17 '17 at 12:48
0

Although this question is merely asking for a workaround, this is listed as the duplicate of many questions that are asking why this happens, so I think it's worth giving an explanation. According to https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06:

The order of word expansion shall be as follows:

Tilde expansion, parameter expansion, command substitution, and arithmetic expansion shall be performed, beginning to end.

When the shell evaluates the string cd $a, it first performs tilde expansion (which is a no-op, since $a does not contain a tilde), then it expands $a to the string ~/Foo, which is the string that is finally passed as the argument to cd.

William Pursell
  • 174,418
  • 44
  • 247
  • 279
-1

If you use double quotes the ~ will be kept as that character in $a.

cd $a will not expand the ~ since variable values are not expanded by the shell.

The solution is:

eval "cd $a"

danron
  • 179
  • 8