用作路径前缀的〜(波浪号)是什么?


11

编辑:这是/programming/998626/含义-tilde-in-linux-bash-not-home-directory /的副本。我没有以重复形式结束此问题的声誉。

我指的不是~主目录,而是这样的:

$ ls ~foo/bar
/some/mount/point/foo/bar

但是,如果我尝试使用不同的挂载点,例如:

$ mount | ag "/dev "
devfs on /dev (devfs, local, nobrowse)
$ ls /dev/stdin
/dev/stdin
$ ls ~stdin
zsh: no such user or named directory: stdin . 
# bash has a similar error message: 
ls: ~stdin: No such file or directory

~在这种情况下,所谓的是什么?它是如何工作的?

编辑:基于以下一些评论的更多信息:

  1. 我可以证明这foo不是我系统上的用户名。
  2. 尝试自动完成时,ls -lah ~不会显示所有选项。即我能够cd ~qux,当qux没有显示在自动完成中。再次qux不是我的系统中的用户。
  3. 如果重要的/some/mount/point是网络共享。
  4. 所有的细节都暗示了一些命名路径模型,这是路径扩展的Z外壳功能,但这在bash中也有效,显然不支持Z外壳的命名路径。

5
~foo是用户的主目录foo。如果未指定用户,则当前用户为默认用户。
DopeGhoti

但是在这种情况下,/some/mount/point绝对不是我的主目录。 cd ~带我去/Users/$username/--matches$HOME
RD

1
zsh似乎也使用波浪号来指示命名目录。
DopeGhoti

我也怀疑!除了由于某种原因,我上面发布的一系列命令还可以在bash(bash -c "ls ~foo/bar")中使用-它没有命名目录。此外,即使在zsh中,如果检查env,也看不到任何命名目录的建立。我在Mac OS上,我觉得这是O​​S X特有的功能
RD

1
你只说了~foo。取实际的字符串(不是示例foo)并执行grep "actual username" /etc/passwd。根据bash手册,~text应该仅适用于可能的登录用户名(不一定意味着它实际上能够登录;例如对于系统用户而言~lp)。在我所有的测试中,~string对应的string是用户名。
Sergiy Kolodyazhnyy

Answers:


14

什么是〜foo

bash手册中的报价(重点突出):

如果单词以不带引号的波浪号字符('〜')开头,则第一个不带引号的斜杠(或所有字符,如果没有不带引号的斜杠)之前的所有字符均被视为波浪号前缀。用tilde-prefix引起来,将代字号后的tilde-prefix中的字符视为可能的登录名。

~foofoo完全按照中的指定 扩展到用户的主目录/etc/passwd。注意,这可以包括系统用户名;它不一定表示人类用户,也不一定表示他们可以实际在本地登录(例如,可以通过SSH密钥登录)。

事实上,正如指出的意见bash将使用getpwnam功能。该功能本身是由POSIX标准指定的,因此应该存在于大多数类似于Unix的系统上,包括macOSX。此功能不仅限于此,还/etc/passwd可以搜索其他数据库,例如LDAP和NIS。从第394行开始的bash 源代码tilde.c文件的特别摘录:

  /* No preexpansion hook, or the preexpansion hook failed.  Look in the
     password database. */
  dirname = (char *)NULL;
#if defined (HAVE_GETPWNAM)
  user_entry = getpwnam (username);
#else
  user_entry = 0;

实际例子

在下面,您可以在我的系统上查看使用系统用户名进行的测试。注意相应的passwd输入和结果ls ~username

$ grep '_apt' /etc/passwd
_apt:x:104:65534::/nonexistent:/bin/false
$ ls ~_apt
ls: cannot access '/nonexistent': No such file or directory
$ grep '^lp' /etc/passwd
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
$ ls ~lp
ls: cannot access '/var/spool/lpd': No such file or directory

例如,即使_apt帐户按照输出提示被锁定,passwd -S apt它仍会显示为可能的登录名:

_apt L 11/29/2017 0 99999 7 -1

请注意:这不是macOS特定功能,而是Shell特定功能。


我怀疑外壳程序是否可以依靠找出有关帐户到期日期的任何信息,或者是否锁定了它或扩展代字号时的任何信息。例如,在Linux上,到期日期/etc/shadow与其他受保护的身份验证信息(例如密码)一起存储在中。中的引用passwd仅用于使密码无效:这不会阻止通过其他方式进行身份验证。
ilkkachu

@ilkkachu是的,您是对的。我已经通过添加testuser和修改密码到期日期进行了测试。用户名仍显示在选项卡完成中。我已从答案中删除了这一点
Sergiy Kolodyazhnyy,

1
这个答案的唯一问题是,它引用了bash手册页(似乎是旧页)。OP实际上正在使用zsh(可能已经更清楚了),尽管在注释中提到了bash具有相同的行为。
肯·韦恩·范德林德

2
您可以使用getent passwd foo而不是grep foo /etc/password遵循与外壳相同的查找机制-可能包括@Abigail提到的基于网络的目录服务。
RJHunter '18

1
被接受为“例如LDAP和NIS”位的答案,在我看来这是正确的!
RD

5

总结为什么您会看到的原因~foo/bar,这是因为您foo在系统上有一个名为的用户,并且bar在其主目录中有一个文件夹。

在另一个社区中查看此解决方案,该社区解释了为什么(波浪号)~不仅仅是“主目录”。

如果您bin的系统上有一个用户,那么可以通过以下命令列出bin的主目录的内容:

ls ~bin

您可以尝试的另一件事是在提示符下键入以下命令后使用制表符补全(不要回车,只需使用tab键):

ls -lah ~ tab

查看用户主目录的列表,~如果继续,这些目录将扩展到该目录。的制表符完成的示例输出(截断)ls -lah ~ tab

$ ls -lah ~ [tab]
~antman/            ~games/
~bin/               ~mail/

1
+1以完成制表符。这清楚地揭示了bash可以从中获取所有登录名的所有用户名/etc/passwd。如果用户当然知道他们在系统上拥有的用户名,则可以立即帮助弄清发生了什么情况。
Sergiy Kolodyazhnyy

2
感谢您对bash完成的提示-产生了一些有趣的自动完成结果。尽管~foo完成了自动填充,但我可以证明该foo用户不是系统上的用户(的确不存在/etc/passwd)。此外,所有文件夹/文件都/some/mount/point显示在自动完成功能中,这非常有趣。
RD
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.