~ 总是等于 $HOME

Pyt*_*Nut 42 shell bash environment-variables home

我知道以前可能有人问过这个问题,但我在 Google 上找不到。

给定的

  • Linux内核
  • 没有改变 $HOME 的配置
  • 猛击

~ == $HOME是真的吗?

Sté*_*las 51

重要的是要理解~扩展是 shell(某些 shell)的一个特性,它不是一个神奇的字符,而是指在任何地方使用它的主目录。

它被扩展(由 shell,这是一个用于解释命令行的应用程序),就像$var在执行命令之前在 shell 命令行中使用时在某些条件下扩展到它的值一样。

该特性在 1970 年代后期首次出现在 C-shell 中(Bourne shell 没有,它的前身 Thompson shell 也没有),后来被添加到 Korn shell(一个在 Bourne shell 上构建的较新的 shell) 80 年代)。它最终被 POSIX 标准化,现在可以在大多数 shell 中使用,包括非 POSIX 的fish.

由于它在 shell 中如此广泛地使用,一些非 shell 应用程序也将其识别为主目录。许多应用程序在它们的配置文件或它们自己的命令行 ( mutt, slrn, vim...) 中就是这种情况。

bash特别是(它是 GNU 项目的 shell,在许多基于 Linux 的操作系统中广泛使用),当作为 调用时sh,主要遵循有关扩展的POSIX 规则~,并且在 POSIX 未指定的区域,其行为主要类似于 Korn shell(它是部分克隆)。

虽然$var在大多数地方都进行了扩展(除了在单引号内),~但事后才想到的扩展仅在少数特定条件下进行扩展。

在列表上下文中,在需要字符串的上下文中使用它自己的参数时,它会被扩展。

以下是它在 中扩展的几个示例bash

  • cmd arg ~ other arg
  • var=~
  • var=x:~:x(POSIX 要求,用于诸如PATH, MANPATH... 之类的变量)
  • for i in ~
  • [[ ~ = text ]]
  • [[ text = ~ ]]~在 AT&T中被视为一种模式的扩展,ksh但不是bash自 4.0 以来)。
  • case ~ in ~) ...
  • ${var#~} (虽然不是在其他一些 shell 中)
  • cmd foo=~(虽然不是当作为 调用时sh,并且仅当 左侧的=形状像未加引号的bash变量名称时)
  • cmd ~/x (显然是 POSIX 要求的)
  • cmd ~:x(但不是x:~:xx-~-x
  • a[~]=foo; echo "${a[~]} $((a[~]))" (不在其他一些壳中)

以下是一些未扩展的示例:

  • echo "~" '~'
  • echo ~@ ~~(另请注意,这~u意味着扩展到 user 的主目录u)。
  • echo @~
  • (( HOME == ~ )), $(( var + ~ ))
  • extglob:(case $var in @(~|other))...虽然case $var in ~|other)可以)。
  • ./configure --prefix=~(因为--prefix不是有效的变量名)
  • cmd "foo"=~(在bash,因为引号)。
  • 当调用为sh: export "foo"=~, env JAVA_HOME=~ cmd...

至于它扩展到什么:~单独扩展到HOME变量的内容,或者当它未设置时,扩展到帐户数据库中当前用户的主目录(作为扩展,因为 POSIX 未定义该行为)。

应该注意的是,在 ksh88 和bash4.0 之前的版本中,波浪号扩展在列表上下文中进行了通配(文件名生成):

$ bash -c 'echo "$HOME"'
/home/***stephane***
$ bash -c 'echo ~'
/home/***stephane*** /home/stephane
$ bash -c 'echo "~"'
~
Run Code Online (Sandbox Code Playgroud)

在通常情况下,这应该不是问题。

请注意,因为它是展开的,所以同样的警告也适用于其他形式的展开。

cd ~
Run Code Online (Sandbox Code Playgroud)

如果$HOME以组件开头-或包含..组件,则不起作用。因此,即使它不太可能产生任何影响,但严格来说,应该这样写:

cd -P -- ~
Run Code Online (Sandbox Code Playgroud)

甚至:

case ~ in
  (/*) cd -P ~;;
  (*) d=~; cd -P "./$d";;
esac
Run Code Online (Sandbox Code Playgroud)

(覆盖$HOMElike -, +2... 的值)或简单地:

cd
Run Code Online (Sandbox Code Playgroud)

(ascd带你到你的主目录,没有任何参数)

其他外壳具有更高级的~扩展。例如,在 中zsh,我们有:

  • ~4, ~-, ~-2(带完成)用于扩展目录堆栈中的目录(您cd之前去过的地方)。
  • 动态命名目录。您可以定义自己的机制来决定如何~something扩展。


Mic*_*mer 26

在任何系统上的任何版本的 Bash 中,是的~作为一个术语,它本身被定义为:

$HOME 的价值

所以它总是$HOME与当前 shell 的任何内容相同。还有其他几个波浪号扩展,例如~userforuser的主目录,但一个单独的未加引号~将始终扩展到"$HOME".

需要注意的是该行为~,并$HOME在某些情况下是不同的:尤其是,如果$HOME包含空格(或其他IFS字符),然后$HOME(不带引号)将扩大到多个字,而~始终是一个字。~等效地扩展为"$HOME"(引用)。

关于你的具体问题:

[[ $HOME == ~ ]]
Run Code Online (Sandbox Code Playgroud)

总是正确的,因为[[ 抑制分词。[[ ~ == $HOME ]如果其中HOME包含模式匹配字符,则可能不是,但[[ ~ == "$HOME" ]](即,引用"$HOME")始终为真。对于HOME包含空格或特殊字符的值,在单括号内使用它可能会导致语法错误。对于任何合理的主目录配置~"$HOME"都相同并比较相等。


Stéphane Chazelas 在评论中指出了一个案例,其中~$HOME给出不同的值:如果你unset HOME,那么当你使用~Bash 时将调用getpwuid从密码数据库中读取一个值。您没有配置更改的情况排除了这种情况$HOME,但为了完整性,我会在此处提及它。

  • 在包括 `bash` 在内的一些 shell 中,如果未设置 `HOME`,`~` 会从 passwd 数据库扩展到用户的主目录。所以在这种情况下,`~` 可能不会扩展到 `$HOME` 的值。 (6认同)
  • @cuonglm 我检查了源代码。它 [最终](http://git.savannah.gnu.org/cgit/bash.git/tree/lib/tilde/tilde.c#n364) [调用](http://git.savannah.gnu.org /cgit/bash.git/tree/variables.c#n664) `get_current_user_info`,它[在所有平台上使用 `getpwuid`,但 Tandem](http://git.savannah.gnu.org/cgit/bash.git/树/shell.c#n1676)。 (4认同)