37

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 ~

1
  • 2
    Related: "A "tilde-prefix" consists of an unquoted <tilde> character at the beginning of a word, followed by all of the characters preceding the first unquoted"
    – Braiam
    Aug 27, 2014 at 21:27

4 Answers 4

50

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'

5
  • 2
    It is worth noting that zsh does (or rather can be configured to) expand ~ after = too. Among other convenient improvements it has over bash.
    – Jan Hudec
    Aug 29, 2014 at 5:06
  • @JanHudec No - it's more complicated; It's not about expanding after =; it is about expanding at the start of the right side pf a variable assignment. That makes our case a little more confusing when looking closely - see my answer. And zsh is not different from bash in this; The description for 'zsh' is a little cryptic: info --subnodes zsh | less +/'14.7.4 Notes' (zsh man pages are missing) Sep 3, 2014 at 11:09
  • 1
    @VolkerSiegel: zsh is very configurable. In my zsh echo -extract=~/test results in -extract=/home/user/test. It is enabled by MAGIC_EQUAL_SUBST options. On a side-note, I do have all zsh man pages just fine.
    – Jan Hudec
    Sep 3, 2014 at 11:50
  • @JanHudec No doubt about zsh being configurable - and other improvements; I would have expected that MAGIC_EQUAL_SUBST (covered in 14.7.4) is unset by default, and rarely used; Now, it's obviously more relevant if you have it enabled in general. After I get my example section in good shape, that would make a nice addition... Sep 3, 2014 at 12:44
  • @JanHudec Regarding the zsh man pages, they were missing from the Ubuntu packages for a while, thanks for the hint, did not know that was fixed; Looking at the bug, it's fixed for utopic, but not trusty - that explains it!. (launchpad: all zsh manpages and inline help files are missing and AU: zsh man missing...) Sep 3, 2014 at 12:55
19

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.
3

~ is not a path per se. It's a character that gets special treatment from the shell where ~ or ~/ means "replace with current user's home directory path". ~username means "replace with username's home directory path".

Because it's not a path, it's only recognized in certain places in the command (as the first character of a new space-split token).

When expanded, it it replaced with an absolute path.

Using $HOME works because HOME is just a variable set by the shell and follows normal shell rules for variable replacement (it happens before the input is split on spaces and executed).

1

You are correct. the ~/Downloads is the same as /home/username/Downloads.

Some installers and extractors are just very picky about where it needs to put things. I think it might be because it logs the filepaths, and the logs wont accept ~ in an accepted path.

I just got used to typing in /home/username instead. :)

You must log in to answer this question.

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