bash $ {VAR // поиск / замена} и странное поведение регулярных выражений

8

Я пытаюсь выполнить поиск и замену переменной, используя расширение параметра $ {VAR // search / replace}. У меня довольно длинная и злая PS1, которую я хочу отработать размером после расширения. Для этого я должен удалить кучу escape-последовательностей, которые я вставляю в него. Однако, пытаясь удалить все последовательности ANSI CSI SGR, я столкнулся с проблемой с моим синтаксисом.

Учитывая мой PS1 из:

PS1=\[\033]0;[\h] \w\007\]\[\033[1m\]\[\033[37m\](\[\033[m\]\[\033[35m\]\u@\[\033[m
\]\[\033[32m\]\h\[\033[1m\]\[\033[37m\]\[\033[1m\])\[\033[m\]-\[\033[1m\](\[\033[m
\]\t\[\033[37m\]\[\033[1m\])\[\033[m\]-\[\033[1m\](\[\033[m\]\[\033[36m\]\w\[\033[1m
\]\[\033[37m\])\[\033[35m\]${git_branch}\[\033[m\]\n$

(да, это больно, я знаю ...)

Я пытаюсь сделать:

# readability
search='\\\[\\033\[[0-9]*m\\\]'
# do the magic
sane="${PS1//$search/}"

Однако это , кажется, жадный в точке [0-9](почти как [0-9]рассматривается как .вместо этого):

echo "${PS1//$search/}"
\[\033]0;[\h] \w\007\]\n$ 

Если я удаляю *, и изменяю [0-9]на [0-9][0-9](как это более наглядно), я получаю ближе к ожидаемому результату:

$ search='\\\[\\033\[[0-9][0-9]m\\\]'
$ echo "${PS1//$search/}"
\[\033]0;[\h] \w\007\]\[\033[1m\](\[\033[m\]\u@\[\033[m\]\h\[\033[1m
\]\[\033[1m\])\[\033[m\]-\[\033[1m\](\[\033[m\]\t\[\033[1m\])\[\033[m\]-\[\033[1m
\](\[\033[m\]\w\[\033[1m\])$(git_branch)\[\033[m\]\n$ 

Почему *(ноль или более) делают сумасшедшие вещи? я что-то здесь упускаю? Если я пропущу то же самое регулярное выражение через sed, я получу ожидаемый результат:

echo $PS1 | sed "s/$search//g"
\[\033]0;[\h] \w\007\](\u@\h)-(\t)-(\w)$(git_branch)\n$
Драв Слоан
источник
3
Это не регулярное выражение, это просто сопоставление с шаблоном, похожее на глобус файла. extglobдействительно влияет на поведение сопоставления с образцом.
Иордания
Бродяга, вот почему - у меня была догадка, возможно, это имело место: / Я пытался найти разъяснение механизма сопоставления, но без особого успеха. отправляется читать о extglob (похоже на работу для sed!)
Drav Sloan
1
*([0-9])это эквивалент [0-9]*использования extglob.
Иордания
1
Если вы получили правильный ответ, приемлемо ответить на ваш собственный вопрос. Я был счастлив, что дал некоторые рекомендации.
Иордания
2
@DravSloan - эта подсказка болеет! 8-)
СЛМ

Ответы:

6

Похоже, вы хотите удалить вещи между \[и \]:

$ shopt -s extglob
$ printf '%s\n' "${PS1//\\\[*(\\[^]]|[^\\])\\\]/}"
(\u@\h)-(\t)-(\w)${git_branch}\n$

Тем не менее, bashзамена настолько неэффективна, что вам, вероятно, было бы лучше уволить perlили sedздесь, или сделать это в виде цикла:

p=$PS1 np=
while :; do
  case $p in
    (*\\\[*\\\]*) np=$np${p%%\\\[*};p=${p#*\\\]};;
    (*) break;;
  esac
done
np=$np$p
printf '%s\n' "$np"

(это стандартный синтаксис POSIX sh выше, кстати).

И если вы хотите расширенное приглашение от этого:

ep=$(PS4=$np;exec 2>&1;set -x;:); ep=${ep%:}
Стефан Шазелас
источник
4
Ах, мой день завершен, еще одна куча символов старшей магии от Верховного жреца Командной Строки, Стефана. Клянусь, половина ваших сообщений мои глаза настроены на неправильную скорость передачи данных, и я получаю экран беспорядка :) И да, конечная цель состояла в том, чтобы удалить все escape-последовательности: мне не удавалось просто удалить между [и ], Спасибо!
Драв Слоан
5

После некоторых указаний от jordanm (и прочтения раздела «Сопоставление с образцом» на справочной странице bash) выясняется, что эти шаблоны, используемые расширением параметров, не являются регулярными выражениями. Однако для моего конкретного случая, если shopt extglobвключен, я могу сделать:

search='\\\[\\033\[*([0-9])m\\\]'

где так *([0-9])же, как[0-9]* в регулярных выражениях.

Похоже, что extglob предоставляет некоторые механизмы, схожие с regex (из страницы руководства bash):

          ?(pattern-list)
                 Matches zero or one occurrence of the given patterns
          *(pattern-list)
                 Matches zero or more occurrences of the given patterns
          +(pattern-list)
                 Matches one or more occurrences of the given patterns
          @(pattern-list)
                 Matches one of the given patterns
          !(pattern-list)
                 Matches anything except one of the given patterns
Драв Слоан
источник
2
Да, extglobреализует подмножество kshрасширенных глобусов. ksh93на самом деле есть оператор printf для преобразования между шаблонами и (AT & T) RE ( printf '%P\n' '\\\[[0-9]*\\\]'дает *\\\[*([0-9])\\\]*)
Стефан Шазелас
Хм, похоже, что * [0-9] работает в других запросах регулярных выражений (без круглых скобок).
Macieksk
0

Pure Bash поддерживает полный диапазон последовательностей ANSI

# Strips ANSI CSI (ECMA-48, ISO 6429) codes from text
# Param:
# 1: The text
# Return:
# &1: The ANSI stripped text
strip_ansi() {
  echo -n "${1//$'\e'[@A-Z\[\\\]\^_]*([0-9:;<=>?])*([ \!\"#$%&\'()\^*+,\-.\/])[@A-Z\[\\\]\^_\`a-z\{|\}~]/}"
}
Леа Гри
источник