前言

本篇并没有什么科技含量,权当参考。

严格来说,Bash是以Shell扩展(Shell Expansions)的形式来支持波浪线的,即波浪线扩展(Tilde Expansion)。而我们所熟知的~对应HOME,也只是这个扩展支持的一部分。

~/开头或单独~的处理

在Bash中输入的参数以~/开头,或者仅有~都会被预处理替换为环境变量HOME

例如:

zt@zt-Ubuntu:~$ echo ~
/home/zt
zt@zt-Ubuntu:~$ echo ~/Test
/home/zt/Test

那么Bash又是如何将~替换为HOME的呢?可以看看相关源码:

char *tilde_expand_word (filename) const char *filename;
{
  char *dirname, *expansion, *username;
  int user_len;
  struct passwd *user_entry;

  if (filename == 0)
    return ((char *)NULL);

  if (*filename != '~')
    return (savestring (filename));

  /* A leading `~/' or a bare `~' is *always* translated to the value of
     $HOME or the home directory of the current user, regardless of any
     preexpansion hook. */
  if (filename[1] == '\0' || filename[1] == '/')
    {
      /* Prefix $HOME to the rest of the string. */
      expansion = sh_get_env_value ("HOME");
#if defined (_WIN32)
      if (expansion == 0)
    expansion = sh_get_env_value ("APPDATA");
#endif

      /* If there is no HOME variable, look up the directory in
     the password database. */
      if (expansion == 0)
    expansion = sh_get_home_dir ();

      return (glue_prefix_and_suffix (expansion, filename, 1));
    }
...
}

源码很清晰,这里简单解释下:

  1. 如果参数为空,返回NULL;否则
  2. 如果参数不以~开头,则正常处理,不进行波浪线扩展;否则
  3. 如果为单独的~或为~/,则将~替换为环境变量HOME(Windows下替换为APPDATA),如果HOME不存在,则从/etc/passwd中寻找;否则
  4. 其他匹配处理。

其他匹配处理

如果上述情况不满足就开始匹配波浪线扩展里的其他情况啦,这里列举几种情况:

~+

替换为Shell变量PWD,即当前工作目录(./)。如:

zt@zt-Ubuntu:~$ cd 桌面
zt@zt-Ubuntu:~/桌面$ echo ~+
/home/zt/桌面

~-

替换为Shell变量OLDPWD,即上次工作目录。如:

zt@zt-Ubuntu:~$ cd 桌面
zt@zt-Ubuntu:~/桌面$ echo ~-
/home/zt

~X

这里X为某些字符,非字母X。此时Bash会从/etc/passwd寻找匹配项。如:

zt@zt-Ubuntu:~$ echo ~zt
/home/zt
zt@zt-Ubuntu:~$ echo ~root
/root
zt@zt-Ubuntu:~$ echo ~mail
/var/mail
zt@zt-Ubuntu:~$ echo ~bin
/bin

参考