定义变量时带引号和不带引号有什么区别?

定义变量时带引号和不带引号有什么区别?

如果我用引号定义一个变量:

TEMP="~/Dropbox"

然后

ls $TEMP

不起作用,相反

echo $TEMP | ls

作品。

为了获得相同的结果,我还可以定义不带引号的变量,例如:

TEMP=~/Dropbox

这样我就可以简单地输入

ls $TEMP

我对它们之间的区别很困惑,我想知道我应该在何时何地分别使用这两种定义?

答案1

TEMP="~/Dropbox"

上面定义了一个变量,其中包含一个文字波浪号,后跟一个斜杠。因为它在引号中,所以 shell 不会扩展~/到主目录。观察:

$ echo "quotes=~/" noquotes=~/
quotes=~/ noquotes=/home/john1024/

因此,如果您想~/表示主目录,则~/需要在引号之外。以下作品:

TEMP=~/"Dropbox"

让我们考虑一下如果将~/放在引号中会发生什么:

TEMP="~/Dropbox"
ls $TEMP

上面的命令将查找该文件,该文件表示名为 的目录中~/Dropbox名为 的文件。由于您很可能没有名为 的目录,因此将返回一条错误消息:“没有这样的文件或目录。”Dropbox~~ls

另外,下面的命令不会执行您的想法:

echo $TEMP | ls

ls忽略标准输入。裸ls命令仅列出当前目录中的文件,无论该目录是什么。

答案2

man bash 并寻找“波浪线扩展”

或者简而言之:未加引号的波形符 (~) 被替换为用户的 home,如果它是变量的一部分,则不会再发生这种情况。

$ T=~/Downloads
$ echo $T
/home/zstegi/Downloads

$ T="~/Downloads"
$ echo $T
~/Downloads

答案3

这个问题有几个方面。首先,带引号的赋值语句的效果可能与不带引号的相同赋值语句不同,原因有二:

  1. 如果赋值包含分隔符句法代币(例如;<>或 空白)那么引号可能会包含这些内容,而未加引号的赋值语句将在标记处进行分隔。

    • bash和其他一些 shell 中,标记()是特殊情况,用于var=(...)指示数组分配,但在不支持数组扩展的 POSIX shell 中,如果没有引号,相同的构造将是语法错误。

      (   spc=  sc=; echo "<$spc>:<$sc>"
          spc=' ' sc=\;; echo "<$spc>:<$sc>"
      )
      

      <>:<>
      < >:<;>
      
  2. 如果赋值包含扩展标记,引号可能会阻止扩展的发生,否则在没有它们的情况下可能会发生扩展。

    • 包含的任何令牌之内扩展不适用,因为扩展的内容实际上是从扩展传递到分配的变量。
    • 双引号"不能用于$表示的 shell 扩展,因此在赋值语句中通常是无关的,尽管它可以用于包含$表示的扩展一个引用上下文中的其他 shell 标记。

      (   spc=\  sc=";" 
          tkns=$(printf "(<\n\'\t)>|")$spc$sc\"
          names=tkns$spc$"sc$spc$"spc
          printf ::$%s::\\n "$names" "$tkns"
      )
      

      ::$tkns $sc $spc::
      ::$(<
      \'  )>| ;"::
      

Shell 标记对于 shell 来说是非常特殊的字符 - 它们是将命令彼此分隔开的字符,以及将命令字与命令参数分隔开的字符。它们是分隔/连接 shell 单词的字符。 Shell 标记是 shell 的字符句法解析器(或者词法分析器在 shell 有机会考虑扩展命令的任何部分之前,它将在读取命令时查找并执行操作。

但还有其他角色几乎对于外壳来说是特殊的。这些是影响 shell 扩展的字符,例如 shell$IFS参数值中包含的字符,以及文件名扩展元字符,如?[*、 和 - 正如您的问题中所述 -~波形符。

除了特殊情况下的波形符外~,这些字符与赋值语句无关 -任务语境(相对于列表语境)不受$IFS分割和文件名扩展的影响。因此,变量赋值中发生的扩展不需要像在命令列表中那样对文字值进行保护。

所以下面的作品......

(meta=*?; meta=[$meta; echo "$meta")

[*?

不过,波形~符是一种特殊的 shell 扩展。它的扩展处理方式非常像 shell,alias因为它的内容总是按字面扩展。~波形符和alias扩展都 - 就像更典型的$美元符号表示扩展任务上下文 - 是总是不受$IFS文件名扩展的影响。但它们的不同之处在于,~波形符几乎总是作为参数展开,并且 shellalias不能在该上下文中展开,除非它紧跟在另一个alias已展开且其值以 <space> 结尾的 shell 后面。

波形符扩展~不会发生在任何类型的引号内,并且就这一点而言,根本不会发生,除非它在所有方面都被正确定界,或者除非其尾随上下文命名了系统用户并且 shell 可以正确确认这一事实。

所以...

~some_user

...将扩展到用户的主目录some_user如果 shell 可以确认这样的用户存在,否则它根本不会扩展。

正确界定波形符扩展~您可以使用已经提到的任何 shell 标记,除了引号以及/右侧的a列表上下文,或者,在任务上下文,或者/:右侧,或者在左侧,=赋值语句中的第一个或:其他任何地方。

当没有尾随上下文并且~波浪号被正确分隔时,~波浪号将扩展到 shell 变量的当前值$HOME。在讨论波浪号扩展时,这种区别经常被错误地忽视~。用户的主目录和值$HOME不需要相同,因为 shell 变量$HOME可以重新分配或unset像其他任何时间一样随时重新分配。事实上,当$HOME unset~, POSIX 保留了单独波形符扩展的行为未指定但是,bash例如,将尝试在系统中查找当前用户的主目录。

例如:

HOME="

:~"::~//~ bash -c '
    printf "\t<%s>" ~/ ~mikeserv/
    HOME=; echo
    printf "\t<%s>" ~/ ~mikeserv/
    unset HOME; echo
    printf "\t<%s>" ~/ ~mikeserv/
'

    <

:~::/home/mikeserv//~/> </home/mikeserv/>
    </> </home/mikeserv/>
    </home/mikeserv/>   </home/mikeserv/>

相关内容