2016-12-06 68 views
21

我试图理解为什么猛砸删除双引号(而不是单引号),这样做的变量扩展与${parameter:+word}使用替代值)时,一个在这里的文档中,例如:

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

根据在手册中,:+之后的“单词”通过代字符扩展,参数扩展,命令替换和算术扩展进行处理。这些都不应该做任何事情。

我缺少什么?如何在扩展中获得双引号?

+1

似乎是一个错误或执行泄漏。这是不可能用双引号作为备用值,而无需将其分配给一个变量:'DQ = '“'; ... $ {VAR:+ $ DQ}' – choroba

+1

@choroba:'$ {VAR:+ $(回波'''')}'但我基本同意。也有一些是在word'的'解析很奇怪的报价参数扩展(在这里参数扩展文档展开仿佛报价,根据手册,这似乎是这样。) – rici

+1

的这里几乎等价的文档可以被看作是一个回声“...”,在这种情况下,只需在命令提示符处输入'echo“$ {var:+”Hi there“}”'就可以将其删除。 \“但HERE文档不一致,并产生”在输出而不是“。有些东西显然不正确。 – Gunstick

回答

4

这种行为看起来是故意的 - 它在我尝试的所有Bourne shell中是一致的(例如,ksh93和zsh的行为方式相同)。

这种行为相当于将这里的doc视为双引号,对于这些特殊的扩展只有。换句话说,你得到

$ echo "${var:+"hi there"}" 
hi there 
$ echo "${var:+'Bye'}" 
'Bye' 

相同的结果只有在POSIX规范我发现一些特别的东西发生在参数扩展双引号的话非常微弱的暗示。这是从informative "Examples" section of Parameter Expansion

模式的双引号是不同的,这取决于放置双引号的位置。

"${x#*}"
<星号>是一个模式字符。
${x#"*"}
字面<星号>被引用而不是特殊的。

我会读取最后一行作为暗示,报价清除双引号适用于该词。这个例子对于单引号是没有意义的,并且通过省略,对于单引号没有引号删除。

更新

我试图在FreeBSD /bin/sh,这是从一个Almquist壳牌的。这个shell输出单引号和双引号。所以全部炮弹的行为不再一​​致,只有我试过的炮弹最多炮弹。

至于:+后获得双引号的字的扩大,我的看法是

$ var=1 
$ q='"' 
$ cat <<EOF 
${var:+${q}hi there$q} 
EOF 
"hi there" 
3
$ cat <<EOF 
${var:+bare alt value is string already} 
${var:+'and these are quotes within string'} 
${var:+"these are double quotes within string"} 
${var:+"which are removed during substitution"} 
"${var:+but you can simply not substitute them away ;)}" 
EOF 
bare alt value is string already 
'and these are quotes within string' 
these are double quotes within string 
which are removed during substitution 
"but you can simply not substitute them away ;)" 

注意,是不需要在这里文档重现此:

$ echo "${var:+'foo'}" 
'foo' 
11

tl; dr

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

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


Jens's helpful answerPatryk Obara's helpful answer都阐明了进一步说明此问题。

注意,问题行为同样适用于

  • 定期双引号字符串(如其他答案说明)(例如,echo "${var:+"Hi there"}";为第1解决方法,你会必须\ -quote围绕"实例;例如,echo "\"${var:+Hi there}\"";好奇地,因为Gunstick指出在一个问题的评论,使用\"在替代值产生"输出确实工作在双引号字符串 - 例如,echo "${var:+\"Hi th\"ere\"}" - 不像在不带引号这里-文档)

  • 相关的扩展${var+...}${var-...}/${var:-...},并${var=...}/${var:=...}

  • 此外,还有一个相对于\-处理相关的奇怪内部双引号字符串/双引号字符串内的双引号替代值here-doc:bashksh意外地删除嵌入\实例;例如,
    echo "${nosuch:-"a\b"}"意外地产生了ab,即使echo "a\b"在分离产量为a\b-见this question

的行为没有解释[1] ,但我可以提供务实的解决方案,与所有主要的POSIX兼容的外壳(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 - 值得称赞的例外 -,其中\实例删除。在zsh,奇怪的是,欲以\"断扩张完全(如做不平衡转义的人在所有的炮弹)。

    • 更具体地说,双引号有的替代值没有句法功能,但他们解析仿佛根本:引用删除应用,而且不能使用(非平衡)"实例在不\"内部-escaping它们(其中,如上所述,是无用的,因为\实例被保留)。

鉴于隐式双引号串的语义,文字$实例必须是\$转义,或一个命令替换必须用于嵌入单个引号字符串($(printf %s '...'))。

+0

我觉得嵌入式'“'被视为文字,然后删除离开时,bash的价值进行评估。但是,让我好奇,为什么前缀/后缀去除不行为一致。我想我会需要寻找到bash的源代码解开,真正发生的事情...... –

+0

@PatrykObara:正是这种不一致性:双引号在替代值中没有语法_function_,但它们被_parsed就好像它们一样__:引用移除被应用,正如你所描述的那样,并且你不能在内部没有'''' - 转义它们(这是没用的,因为'\'被保留)。 – mklement0

+2

@PatrykObara:这种差异可能是意想不到的,但在前缀/后缀删除和字符串替换的引用中,_does_提供了一个有用的功能:区分模式元字符(例如'*')和文字(例如'“*”' ),所以我不会称之为不一致。 – mklement0