【问题标题】:Should I use quotes in environment path names?我应该在环境路径名中使用引号吗?
【发布时间】:2016-01-23 22:39:13
【问题描述】:

我正在清理我的所有配置文件,以使它们尽可能可读。我一直在寻找有关在导出路径时使用引号的样式指南,例如,~/.bashrc 文件:

export PATH="/users/me/path:$PATH"

export PATH=/users/me/path:$PATH

Google shell style 指南建议避免路径名使用引号。相比之下,许多流行的 dotfiles 存储库(例如 Zach Holman 的 here)都使用引号。是否有在路径中使用引号的优势?

【问题讨论】:

  • 我认为当目录名称包含空格时应该使用引号。
  • 在这种情况下,您必须使用引号。如果没有引号,shell 将执行分词和路径名扩展(您当然不希望这样)。请注意,虽然PATH 肯定已经导出,所以您不需要再次exportPATH=/users/me/path:$PATH(这次不加引号)就够了。
  • Google 还要求一般文件名,特别是路径中的目录,只包含可移植文件名字符集中的字符。这意味着只有(拉丁)字母、数字、下划线、破折号、点。当该规则适用时,省略引号是安全的。只有当人们在目录名称中包含奇怪的字符时,引用才成为必要。

标签: bash shell dotfiles


【解决方案1】:

我在 docker .env 文件中设置环境路径名时使用了上面的这些答案,并且得到了一点。我把这个放在这里,供其他人寻找如何为 docker 定义环境变量。

Docker compose 从 .env 文件中读取环境变量,该文件位于运行 docker compose 的同一文件夹中,如此处所述https://docs.docker.com/compose/env-file.

但是,docker compose 不需要将值包含在引号中,而是需要定义不带引号的环境变量,除非引号是值的一部分。同样,如上面的网址所述

引号没有特殊处理(即它们将 VAL 的一部分,您已被警告;)

我试图将NODE_PATH=./src 设置为在docker 部署的react 应用程序中工作的绝对路径,但将其写为NODE_PATH="./src"。这个警告把我从 4 小时的兔子洞里拉了出来。

【讨论】:

  • 谢谢你!进行了一些调试以找出它包括引号。
  • 嗯,我只是在更改配置文件后在开发中体验了它。疯狂的部分是生产运行在那个精确的配置文件上,因为 docker-compose 读取 .env 文件并且一切都很好。 Development no throws unkown character ''' 现在我又迷失在那个开发者最坏的情况下:它不应该工作,但它确实有效。
【解决方案2】:

@gniourf_gniourf@chepner 致敬以寻求帮助。

tl;dr

安全起见,双引号:它适用于所有情况,适用于所有类似 POSIX 的 shell。

如果要添加基于~ 的路径,有选择地保留~/ 未引用以确保~ 被扩展;例如:export PATH=~/"bin:$PATH"变量赋值中~扩展的规则见下文。
或者,只需在单个双引号字符串中使用 $HOME
export PATH="$HOME/bin:$PATH"


注意:以下内容适用于 bashkshzsh,但不适用于(大部分)严格符合 POSIX 标准的 shell,例如 dash;因此,当您定位 /bin/sh 时,您必须用双引号将 export 的 RHS 引用[1]

  • 双引号是可选,仅当您的 RHS 的 literal 部分(要分配的值)既不包含空格也不包含其他 shell 元字符时。强>
  • 引用的变量的值是否包含空格/元字符很重要 - 见下文。
    • 再次重申:当使用export 时,它确实sh 有关,因此请始终在此处使用双引号。

在这种情况下你可以不用双引号就可以逃脱的原因是,在类似 POSIX 的 shell 中 variable-assignment 语句解释它们的 RHS 不同于 em>arguments 传递给 commands,如 POSIX 规范的section 2.9.1 中所述:

  • 具体来说,即使执行 initial 分词,它也仅适用于 未扩展(原始)RHS (这就是为什么您确实需要在文字中用空格/元字符引用,而不是引用它的结果

  • 仅适用于真正的形式的赋值语句
    <name>=<value>所有类似POSIX的shell
    ,即,如果变量名前没有命令名;请注意,这包括为命令定义临时环境变量的分配前置,例如foo=$bar cmd ...

  • 为了安全起见,在其他命令的上下文中的赋值应始终用双引号

    • 对于sh(在(大部分)严格符合POSIX 的shell 中,例如dash),带有export 的赋值被视为常规命令,而@ 987654345@ 部分被视为export 内置函数的第一个参数,因此按常规处理(也受到结果的分词)。
      (POSIX 没有指定任何其他涉及(显式)变量赋值的命令;declaretypesetlocal 是非标准的扩展

    • bashkshzsh,在可以理解的与 POSIX 的偏差中,将赋值逻辑也扩展到 export foo=$bartypeset/declare/local foo=$bar。换句话说:bashkshzshexport/typeset/declare/local 中的命令被视为分配,因此引用并不是绝对必要的

      • 也许令人惊讶的是,dash,它也选择实现 -POSIX local 内置[2] , 不会将赋值逻辑扩展到它;但是,它与其export 行为一致。
    • 传递给env(例如env foo=$bar cmd ...)的分配也可以作为命令参数进行扩展,因此需要双引号 - zsh 除外。

      • env 在这方面与kshbash 中的export 行为不同是因为env 是一个外部实用程序,而exportshell 内置
        zsh 的行为从根本上与其他 shell 在未引用变量引用方面的行为不同)。
  • 波浪号 (~) 扩展在真正的赋值语句中发生如下

    • 除了~ 需要不引用,像往常一样,它也只适用:
      • 如果整个 RHS 是~;例如。:
        • foo=~ # same as: foo="$HOME"
      • 否则:如果满足以下条件两个
        • 如果~ 开始字符串或前面有一个未引用 :
        • 如果~ 后跟一个未引用 /
        • 例如,
          foo=~/bin # same as foo="$HOME/bin"
          foo=$foo:~/bin # same as foo="$foo:$HOME/bin"

示例

这个例子表明,在bashkshzsh 中,即使使用export,您也可以不使用双引号,但我不这样做推荐它

#!/usr/bin/env bash
# or ksh or zsh - but NOT /bin/sh!

# Create env. variable with whitespace and other shell metacharacters
export FOO="b:c &|<> d"

# Extend the value - the double quotes here are optional, but ONLY 
# because the literal part, 'a:`, contains no whitespace or other shell metacharacters.
# To be safe, DO double-quote the RHS.
export FOO=a:$foo # OK - $FOO now contains 'a:b:c &|<> d'

[1] 正如@gniourf_gniourf 指出的那样:使用export 修改PATH 的值是可选的,因为一旦将变量标记为导出,您就可以使用用于更改其值的常规分配 (PATH=...)。
也就是说,您仍然可以选择使用export,以便明确表示正在修改的变量已导出。

[2] @gniourf_gniourf 指出,POSIX 标准的未来版本可能会引入 local 内置函数。

【讨论】:

  • +1 用于参考 POSIX 第 2.9.1 节,并(间接)解释为什么 FOO="${SOMEPATH}/bar.txt" ; cat ${FOO} 不需要(双)引用,即使 ${SOMEPATH} 恰好包含空格。
  • 谢谢,@ChristopherSchultz - 不过,${FOO} 周围确实需要双引号 :)
  • 嗯...似乎我在尝试构建包含命令可选参数的环境变量时不走运,例如[ predicate ] &amp;&amp; OPTARG="-f ${MYPATH}/file" ; command ${OPTARG}。您能否在答案中添加一个示例来解释和/或处理这种特殊情况?
  • @ChristopherSchultz:这确实是一个单独的问题,所以这里只是一个快速指针:在bash 中,使用 array 来存储您的参数,然后传递该数组在双引号中,如here 所示。唯一可移植的解决方案是 - 有风险 - 使用 eval
  • 另外一件事是,如果您的密钥有一个井号标签 # 而您没有引用,那么 # 之后的所有内容都将被解释为注释并被忽略!
【解决方案3】:

test 123 是 UNIX 上的有效路径名。试试

PATH=test 123

它会返回:

123: command not found

甚至

export PATH=test 123

会返回的

bash export: `123': not a valid identifier

它回答了你的问题吗?

老实说,我不会遵循这样的第四方风格指南。虽然我很惊讶,即使是 Google 也会宣传这种错误的建议。

我会跟随:

(需谨慎扩展)

【讨论】:

  • TLDP 的指南也是第三方的,它们在很多方面也存在偏见,例如,在 bashism 明显优越的时候避免 bashism。无论如何,shell 脚本非常灵活(非常简单的例子:你是否在变量名周围使用花括号?),最后它通常归结为自己的判断。
  • 我同意,但是在这种情况下 Google 风格指南 是错误的。我应该称之为第四方..让我改变一下
  • 好吧,只有当你的文字部分包含空格(这首先是不好的做法)并且你很困惑以用可见的空格写下作业时,这是错误的......跨度>
  • 我应该简单地解释一下 分词 是在没有引号的情况下发生的,这意味着什么。我承认这不是我最好的答案。如果以后有时间我会给出适当的解释,否则删除它。
  • 查看 mkelement0 的更新答案。它有一个可执行的例子。一件事我记错了:在 bash 中,即使赋值前面有 declare 变体也没关系。
猜你喜欢
  • 1970-01-01
  • 2011-06-11
  • 1970-01-01
  • 1970-01-01
  • 2012-07-14
  • 2019-04-28
  • 2020-10-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多