Is the tilde, `~` considered to be a relative path?

I'm trying to extract the different part of the Nvidia cuda library installer. I'm using the following command:

mkdir ~/Downloads/nvidia_installers
./cuda_6.5.14_linux_64.run -extract=~/Downloads/nvidia_installers

And I get the following message:

ERROR: extract: path must be absolute.

And when I type the command with the literal address of my home it work perfectly.

./cuda_6.5.14_linux_64.run -extract=/home/likewise-open/XXX/username/Downloads/nvidia_installers

I'm confused shouldn't ~ be the same of /home/likewise-open/XXX/username?

Tested:

./cuda_6.5.14_linux_64.run -extract=$HOME/Downloads/nvidia_installers

and it works, but I don't know why it doesn't allow ~


Solution 1:

Bash only expands a ~ if it's the beginning of a word. You can see this between the following commands:

$ echo -extract=~/test
-extract=~/test

oli@bert:~$ echo -extract ~/test
-extract /home/oli/test

Bash looks for standalone ~ characters and ~/ for this substitution. No other combination or quoted version will work.

$HOME works because variable substitutions are more robust (the $ is a special character whereas ~ is very much less so):

$ echo Thisisastring${HOME}awrawr
Thisisastring/home/oliawrawr

While we're talking about ~, it actually has another couple of other substitution uses:

  • ~+ the current working directory (read from $PWD)
  • ~- the previous working directory (read from $OLDPWD)

As with plain ~, these can have additional paths tacked on the end, and again, these have to be the prefix to a word or Bash will ignore them.

You can read more about this in man bash | less -p ' Tilde'

Solution 2:

Just fixing it

This command shows an error message "ERROR: extract: path must be absolute":

./cuda_6.5.14_linux_64.run -extract=~/Downloads/nvidia_installers

The error is not helpful - the programm was too confused already.
You already know the error is from the ~, as it works with $HOME instead.

The problem: ~ only gets replaced at the start of a word.

For example, this works with the tilde:

echo -extract ~/Downloads

If you need the option syntax with =, using $HOME instead of ~ is the most clean solution;

echo -extract=$HOME/Downloads

The practice

What you should know:

There are special cases where ~ get's expanded when not at the beginning of a word: as part of a variable assignment, directly after the =. Which is confusing here, of course.

The other important special case it for use with variables like PATH. In variable assignments, ~ is also expanded after :, like after the first =.

$ dir=~ sh -c 'echo D2: $dir'
D2: /home/user
$ sh -c 'echo D2: $dir' dir=~
D2: 
$ echo Dir: $dir
Dir:
$ dir=~; sh -c 'echo D2: $dir'
D2: 
$ echo Dir: $dir
Dir: /home/user
$ sh -c 'echo D2: $dir'; d3=~
D2: 
$ echo d3: $d3
d3: /home/user

The meaning of the tilde

In a shell, ~, the tilde, is not really a path. It is only replaced by a path, $HOME, some times.

It is something like a shorthand, or abbreviation, provided by the shell.
It can not be used like a path in general, the shell "expands" it to a path only in very special places.
And even if it is expanded, it can be to something else than the home directory.

  • It is only expanded at the beginning of a word, or in a variable assignment after a : or =
  • It is only expanded if it is not inside quotes
  • It is only expanded to $HOME if there are no further characters in the word before a /

The problem in the command line

According to this, the problem in your command is that the tilde in

-extract=~/Downloads/nvidia_installers

is not expanded, because it is not one of the cases listed. That's all.

The solution could be to make the tilde the first unquoted character of a word, with no other character before the next / - that is just what you get when you use an option with a space before the option argument:

-extract ~/Downloads/nvidia_installers

Another solution would be to use $HOME instead. In a script, that is usually the better choice.

-extract=$HOME/Downloads/nvidia_installers

The error message

But how does the error message
"ERROR: extract: path must be absolute."?
fit into all this?

We know that the tilde did not get expanded. That means the program got the argument text including the ~, but without the /home/auser as the path. That path is ~/Downloads/nvidia_installers - but now there is no shell, so the tilde has no special meaning. It is just a normal directory name. And as every other path of the form foo/bar/baz, it is a relative path

Other uses

If there are characters after the ~, as in ~alice - with all the other rules above applying - and there is a user names alice, that is expanded to the home directory of alice instead, say home/alice.
Also, if you are bob, ~ would expand to /home/bob, and ~bob would expand to the same.

The variant ~+ is expanded to the current directory, $PWD

To refer to the previous directory, where you were before the last cd, you can use ~-, which is expanded to $OLDPWD.

If you use pushd and popd, instead of cd, you will already know that the directory stack can be accessed like ~-2.

Details

All the cases where ~ is expanded to a path are handeled by the shell. For other programs, ~ is just a normal filename character.

For the exact definition inside the shell, here is the relevant section of man bash
Note how replacing ~ by $HOME is just one special case of many cases: "If this login name is the null string, the tilde is replaced with the value of the shell parameter HOME.":

Tilde Expansion
    If a word begins with an unquoted tilde character (`~'), all of the charac‐
    ters  preceding the first unquoted slash (or all characters, if there is no
    unquoted slash) are considered a tilde-prefix.  If none of  the  characters
    in  the tilde-prefix are quoted, the characters in the tilde-prefix follow‐
    ing the tilde are treated as a possible login name.  If this login name  is
    the  null string, the tilde is replaced with the value of the shell parame‐
    ter HOME.  If HOME is unset, the home directory of the user  executing  the
    shell is substituted instead.  Otherwise, the tilde-prefix is replaced with
    the home directory associated with the specified login name.

    If the tilde-prefix is a `~+', the value of the shell variable PWD replaces
    the  tilde-prefix.   If  the tilde-prefix is a `~-', the value of the shell
    variable OLDPWD, if it is set, is substituted.  If the characters following
    the tilde in the tilde-prefix consist of a number N, optionally prefixed by
    a `+' or a `-', the tilde-prefix is replaced with the corresponding element
    from  the  directory  stack,  as  it would be displayed by the dirs builtin
    invoked with the tilde-prefix as an argument.  If the characters  following
    the  tilde in the tilde-prefix consist of a number without a leading `+' or
    `-', `+' is assumed.

    If the login name is invalid, or the tilde expansion  fails,  the  word  is
    unchanged.

    Each variable assignment is checked for unquoted tilde-prefixes immediately
    following a : or the first =.  In these cases, tilde expansion is also per‐
    formed.   Consequently, one may use filenames with tildes in assignments to
    PATH, MAILPATH, and CDPATH, and the shell assigns the expanded value.