Bash расширение переменной ${var: + "..." } в документе, удаляющем двойные кавычки?

Я пытаюсь понять, почему Bash удаляет двойные кавычки (но не одинарные кавычки) при выполнении расширения переменной с помощью ${parameter:+word} (Использовать альтернативное значение) в документе здесь, например:

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

В соответствии с руководством слово "слово" после :+ обрабатывается с расширением тильды, расширением параметров, подстановкой команд и арифметическим расширением. Ничто из этого не должно ничего делать.

Что мне не хватает? Как можно получить двойные кавычки в расширении?

+22
источник поделиться
3 ответа

ТЛ; др

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

"Hi there"
"Hi there"

Вышеприведенный пример демонстрирует два прагматических обходных пути для включения двойных кавычек в альтернативное значение.
Внедренный подход $(...) более громоздок, но более гибок: он позволяет включать встроенные двойные кавычки, а также дает вам возможность контролировать, следует ли увеличивать значение или нет.


Полезный ответ Йенса и полезный ответ Патрика Обары пролили свет на проблему и еще больше продемонстрировали проблему.

Обратите внимание, что проблемное поведение в равной степени относится к:

  • (как отмечено в других ответах) обычные двойные строки -quote d (например, echo "${var:+"Hi there"}"; для 1-го обходного пути вам придется \ -quote окружать " экземпляры"; например, echo "\"${var:+Hi there}\""; любопытно, как Gunstick указывает в комментарии к вопросу, использование \" в альтернативном значении для продукции " в выводе работает в двойном [ CN00] d строк - например, echo "${var:+\"Hi th\"ere\"}" - в отличие от не цитируемых здесь документов.)

  • связанные расширения ${var+...}, ${var-...}/${var:-...} и ${var=...}/${var:=...}

  • Кроме того, существует связанная странность в отношении \ -handling внутри двойных -quote d альтернативных значений внутри двойной строки -quote d/без кавычек здесь-doc: bash и ksh неожиданно удаляют внедренные \ instances; например,
    echo "${nosuch:-"a\b"}" неожиданно выдает ab, хотя echo "a\b" в отдельности дает a\b - см. этот вопрос.

У меня нет объяснения поведения [1] но я могу предложить прагматичные решения, которые работают со всеми основными POSIX-совместимыми оболочками (dash, bash, ksh, zsh):

Обратите внимание, что " экземпляры никогда не требуются по синтаксическим причинам внутри альтернативного значения: альтернативное значение неявно обрабатывается как двойная строка -quote d: без расширения тильды, разделения слов и смещения не происходит, но расширения параметров, арифметика выполняются расширения и подстановки команд.

Обратите внимание, что в расширениях параметров, включающих подстановку или удаление префикса/суффикса, кавычки имеют синтаксическое значение;например: 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] В действительности, альтернативное значение:

  • ведет себя как неявно двойная строка -quote d,
  • ' экземпляры, как в обычных двойных строках -quote d, рассматриваются как литералы.

Учитывая вышеизложенное,

  • это имело бы смысл рассматривать внедренные " экземпляры как литералы тоже, и просто передать их через, так же, как ' экземпляры.
    Вместо этого, к сожалению, они удаляются, и если вы попытаетесь избежать их как \", \ сохраняется (внутри не цитируемых здесь документов, но, как ни странно, не внутри двойных строк -quote d), за исключением ksh - похвального исключения -, где удаляются \ экземпляры. В zsh, как ни странно, попытка использовать \" нарушает расширение (как и несбалансированные неэкранированные во всех оболочках).

    • Более конкретно, двойные кавычки не имеют синтаксической функции в альтернативном значении, но они анализируются, как если бы они это делали: удаление кавычек применяется, и вы не можете использовать (несбалансированные) " экземпляры внутри" без \" -escaping их (что, как указано, бесполезно, потому что \ экземпляры сохраняются).

Учитывая неявную двойную семантику d-строки -quote, литеральные значения $ instance должны быть либо \$ -escaped, либо для вставки одной строки -quote d должна использоваться подстановка команды ($(printf %s '...')).

+12
источник

Поведение выглядит преднамеренно - оно согласовано во всех оболочках Bourne, которые я пытался (например, ksh93 и zsh ведут себя одинаково).

Поведение эквивалентно рассмотрению здесь-документа как двойного кавычки только для этих специальных расширений. Другими словами, вы получите тот же результат для

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

В спецификации POSIX есть очень слабый намек, что я обнаружил, что что-то особенное происходит для двойных кавычек в разложениях параметров. Это из информативный раздел "Примеры" параметра Expansion:

Двойное цитирование шаблонов отличается в зависимости от места размещения двойных кавычек.

"${x#*}"
       < звёздочка > является характером шаблона.
   ${x#"*"}
       Литерал < звёздочка > цитируется и не является особенным.

Я бы прочитал последнюю строку, предлагая, что предложение цитаты для двойных кавычек относится к слову. Этот пример не имеет смысла для одинарных кавычек, и, упущением, нет исключения цитат для одинарных кавычек.

Обновление

Я попробовал FreeBSD /bin/sh, который был получен из оболочки Almquist. Эта оболочка выводит одиночные и двойные кавычки. Таким образом, поведение больше не является согласованным во всех оболочках, только для большинства оболочек, которые я пробовал.

Что касается получения двойных кавычек в расширении слова после :+, мой выбор

$ var=1
$ q='"'
$ cat <<EOF
${var:+${q}hi there$q}
EOF
"hi there"
+4
источник
$ 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'
+3
источник

Посмотрите другие вопросы по меткам или Задайте вопрос