bash 变量扩展 ${var : +"..."} in here-document removing double quotes?

标签 bash heredoc expansion quoting

我试图理解为什么 Bash 在使用 ${parameter:+word} 进行变量扩展时删除双引号(而不是单引号) (使用替代值),在此处的文档中,例如:

% var=1
% cat <<EOF
> ${var:+"Hi there"}
> ${var:+'Bye'}
> EOF
Hi there
'Bye'

根据手册,:+后面的“词”用波浪号扩展、参数扩展、命令替换和算术扩展进行处理。这些都不应该做任何事情。

我错过了什么?如何在扩展中获得双引号?

最佳答案

tl;博士

$ var=1; cat <<EOF
"${var:+Hi there}"
${var:+$(printf %s '"Hi there"')}
EOF

"Hi there"
"Hi there"

以上演示了两种实用的解决方法,以在替代值中包含双引号。
嵌入式$(...)方法更麻烦,但更灵活:它允许包含嵌入的双引号,还可以让您控制是否应该扩展值。

Jens' helpful answerPatryk Obara's helpful answer两者都阐明并进一步证明了这个问题。

请注意 问题行为同样适用于 :
  • (如其他答案中所述)常规 双引号字符串 (例如, echo "${var:+"Hi there"}" ;对于第一个解决方法,您必须在 \ 周围引用 " 实例;例如, echo "\"${var:+Hi there}\"" ;奇怪的是,正如 Gunstick 在对该问题的评论中指出的那样,使用\" 在输出中产生 " 的替代值中确实适用于双引号字符串 - 例如, echo "${var:+\"Hi th\"ere\"}" - 与未加引号的 here-docs 不同。)
  • 相关扩展 ${var+...} , ${var-...}/${var:-...} , 和 ${var=...}/${var:=...}
  • 此外,还有一个 \ 相关的奇怪之处-处理在双引号字符串内的双引号替代值/未引用的此处文档:bashksh意外删除嵌入 \实例;例如。,echo "${nosuch:-"a\b"}"意外产生 ab ,即使 echo "a\b"独立 yield a\b - 见 this question .

  • 我有 不解释对于行为[1]
    ,但我可以提供务实的解决方案适用于所有主要的 POSIX 兼容 shell ( dashbashkshzsh ):

    请注意 "替代值中的语法原因从不需要实例:替代值被隐式处理为双引号字符串 : 没有波浪号扩展,没有分词,也没有通配符发生,但会执行参数扩展、算术扩展和命令替换。

    请注意,在涉及替换或前缀/后缀删除的参数扩展中,引号确实具有句法含义;例如:echo "${BASH#*"bin"}"echo "${BASH#*'bin'}" - 奇怪的是,dash但是不支持单引号。
  • 如果您想用引号将整个替代值括起来 ,它有 无内嵌引号 你想要它 展开 ,
    引用整个扩展 ,绕过了"的问题从替代值中删除:
    # Double quotes
    $ var=1; cat <<EOF
    "${var:+The closest * is far from   $HOME}"
    EOF
    "The closest * is far from   /Users/jdoe"
    
    # Single quotes - but note that the alternative value is STILL EXPANDED,
    # because of the overall context of the unquoted here-doc.
    var=1; cat <<EOF
    '${var:+The closest * is far from   $HOME}'
    EOF
    'The closest * is far from   /Users/jdoe'
    
  • 对于 嵌入式报价 ,或至 防止替代值的扩展 ,
    使用嵌入式命令替换 (未引用,尽管它的行为就像被引用一样):
    # Expanded value with embedded quotes.
    var=1; cat <<EOF
    ${var:+$(printf %s "We got 3\" of snow at   $HOME")}
    EOF
    We got 3" of snow at   /Users/jdoe
    
    # Literal value with embedded quotes.
    var=1; cat <<EOF
    ${var:+$(printf %s 'We got 3" of snow at   $HOME')}
    EOF
    We got 3" of snow at   $HOME
    

  • 这两种方法可以根据需要结合使用。

    [1]
    实际上,替代值:
  • 行为类似于隐式双引号字符串,
  • '实例,就像在常规双引号字符串中一样,被视为文字。

  • 综上所述,
  • 处理嵌入式 " 是有意义的实例也作为文字,并简单地传递它们,就像 ' 一样实例。
    相反,可悲的是,它们已被删除,如果您尝试将它们作为 \" 转义, \也保留了 (在未引用的此处文档中,但奇怪的是不在双引号字符串中),除了在 ksh - 值得称赞的异常(exception) - ,其中\实例被删除。在 zsh ,奇怪的是,试图使用 \"完全打破扩张 (就像所有 shell 中的不平衡未转义的一样)。
  • 更具体地说,双引号在替代值中没有语法功能,但它们被解析为好像它们确实存在:应用了引号删除,并且您不能使用(不平衡)"没有 \" 的内部实例- 转义它们(如前所述,这是无用的,因为 \ 实例被保留)。

  • 鉴于隐含的双引号字符串语义,文字 $实例必须是 \$ -escaped,或者必须使用命令替换来嵌入单引号字符串 ( $(printf %s '...') )。

    关于bash 变量扩展 ${var : +"..."} in here-document removing double quotes?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40995523/

    相关文章:

    Bash for 循环语法错误与管道

    ruby - 在(双引号)heredoc 中使用 `gsub` 不起作用

    android - 测试APK扩展库的问题

    bash - 如何在 Tomcat6 的初始化脚本中为 "status"命令设置退出代码

    linux - Bash shell 脚本: How to Sum the Values in a File With the Use of For Loop and bc Command

    arrays - 如何在 BASH 中创建 n 个变量?

    c++ - 如何扩展 integer_sequence?

    linux - bash 脚本 Heredoc + FTP 错误

    php - if/else 与 Heredoc 的行为不符合预期

    regex - Bash - 在正则表达式中转义美元的正确方法