Подтвердить что ты не робот

Как избежать истории расширения восклицательного знака! внутри подстановки команды в двойных кавычках типа "$ (echo '! b')"?

EDIT: замена команды не требуется для удивительного поведения, хотя это наиболее распространенный вариант использования. Тот же вопрос относится только к echo "'!b'"

b=a

# Enable history substitution.
# This option is on by default on interactive shells.
set -H

echo '!b'
# Output: '!b'
# OK. Escaped by single quotes.

echo $(echo '!b')
# Output: '!b'
# OK. Escaped by single quotes.

echo "$(echo '$b')"
# Output: '$b'
# OK. Escaped by single quotes.

echo "$(echo '!b')"
# Output: history expands
# BAD!! WHY??
  • В последнем примере, что является лучшим способом избежать !?
  • Почему это не удалось избежать, даже если я использовал одинарные кавычки, тогда как echo "$(echo '$b')" был? В чем разница между ! и $?
  • Почему работала echo $(echo '!b') (без кавычек)? (указана @MBlanc).

Я бы предпочел сделать это без:

  • set +H, так как мне понадобится set -H, чтобы сохранить состояние оболочки

  • escape-обратные сбрасывания, потому что для каждого ! он нужен один, и он должен быть вне кавычек:

    echo "$(echo '\!a')"
    # '\!a'.
    # No good.
    
    echo "$(echo 'a '\!' b '\!' c')"
    # a ! b ! c
    # Good, but verbose.
    
  • echo $(echo '!b') (без кавычек), потому что команда может возвращать пробелы.

Версия:

bash --version | head -n1
# GNU bash, version 4.2.25(1)-release (i686-pc-linux-gnu)
4b9b3361

Ответ 1

В последнем примере

echo "$(echo '!b')"

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

Чтобы исправить это, вам придется временно отключить расширение истории:

set +H
echo "$(echo '!b')"
set -H

Ответ 2

Об этом неоднократно сообщалось как об ошибке, в последнее время против bash 4.3 в 2014 году, для поведения, возвращающегося на bash 3.

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

Он исправлен в bash 4.4, echo "$(echo '!b')" не расширяется, echo "'!b'" делает, что я считаю правильным, потому что одинарные кавычки являются маркерами синтаксиса оболочки в первом примере, а не во втором.

Ответ 3

Если расширение истории включено, вы можете только эхо-символ !, если он помещается в одинарные кавычки, экранирован или сопровождается символом пробела, возвратом каретки или =.

От man bash:

   Only backslash (\) and single quotes can  quote  the  history
   expansion character.

   Several  characters inhibit history expansion if found immediately fol-
   lowing the history expansion character, even if it is unquoted:  space,
   tab,  newline,  carriage return, and =.

Я считаю, что ключевым словом здесь является "Только". В примерах, приведенных в вопросе, рассматриваются только внешние кавычки, являющиеся двойными кавычками.