bash ${VAR//search/replace}と奇妙な正規表現の動作

bash ${VAR//search/replace}と奇妙な正規表現の動作

${VAR//search/replace} パラメーター拡張を使用して、変数の検索と置換を実行しようとしています。私は長くて邪悪なPS1を持っていますが、拡張するとどれだけ大きくなるかを知りたいです。これを行うには、ここに含まれる複数のエスケープシーケンスを削除する必要がありました。しかし、すべての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$ 

なぜ*(0個以上)クレイジーなことをするのですか?ここで何か抜けましたか? sedを介して同じ正規表現を渡すと、期待される結果が得られます。

echo $PS1 | sed "s/$search//g"
\[\033]0;[\h] \w\007\](\u@\h)-(\t)-(\w)$(git_branch)\n$

答え1

\[との間の項目を削除したいようです\]

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

しかし、置換は非常に非効率的であるため、bashここでperlORを実行する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%:}

答え2

jordanmの指示を受けて(そしてbashのマニュアルページの「パターンマッチング」セクションを読んだ後)、パラメータ拡張に使用されるこれらのパターンは正規表現ではないことがわかりました。しかし、私の特別な場合にオンにshopt extglobなっている場合は、次のようにできます。

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

*([0-9])ここでは正規表現と同じです[0-9]*

extglobは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

答え3

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() {
  shopt -s extglob
  printf %s "${1//$'\e'[@A-Z\[\\\]\^_]*([0-9:;<=>?])*([ \!\"#$%&\'()\^*+,\-.\/])[@A-Z\[\\\]\^_\`a-z\{|\}~]/}"
}

関連情報