web-dev-qa-db-ja.com

Bashプロンプトの関数で印刷されない文字をエスケープする

Bashプロンプト(PS1変数)で、プロンプトにテキストを追加する可能性のある関数を呼び出しています:export PS1="\u@\h \$(my_function) \$ "

ただし、プロンプトの関数には、関数の出力に基づいて変化するANSIカラーコードが含まれています(赤の場合もあれば、緑の場合もあります)。 PS1変数に「\[」を追加すると、これらのコードは非印刷としてエスケープされますが、関数でechoを実行すると、「\[」が文字通りプロンプトに出力されます。 。

Bashプロンプトで使用するために関数内からこれらのANSIカラーコードをエスケープするにはどうすればよいですか?

23

readlineライブラリは、\001および\002(ASCII SOHおよびSTX )を印刷不可として受け入れますテキスト区切り文字。これらは、readlineを使用するすべてのアプリケーションでも機能します。

bashソースコードのlib/readline/display.c:243から:

243 /* Current implementation:
244         \001 (^A) start non-visible characters
245         \002 (^B) end non-visible characters
246    all characters except \001 and \002 (following a \001) are copied to
247    the returned string; all characters except those between \001 and
248    \002 are assumed to be `visible'. */

bash固有の\[および\]は、実際には\001および\002に変換されます。 y.tab.c:7640


注:bashprintfまたはecho -eを使用し、テキストに\001または\002数値の直前で、bashバグが発生し、8進数のエスケープを処理するときに1桁を食べすぎてしまいます。つまり、\00142は、正しい8進数01(ASCII)が続く)ではなく、8進数014(ASCII "2")が続く)として解釈されます。 「42」)。このため、代わりに16進数バージョン\x01および\x02を使用してください。

35
user1686

これが素晴らしい完全な答えです。\001などがどこに行かなければならないかを理解するために、私はもっと多くの掘り下げをしなければなりませんでした。お役に立てれば。

# Color Prompt for git
reset=$(tput sgr0)
boldgreen=$(tput setaf 2)$(tput bold)
cyan=$(tput sgr0)$(tput setaf 6)
boldred=$(tput setaf 1)$(tput bold)
boldwhite=$(tput setaf 7)$(tput bold)
boldyellow=$(tput setaf 3)$(tput bold)

PARENCLR=$'\001\e[0;36m\002'
BRANCHCLR=$'\001\e[1;33m\002'

alias branchname="git branch 2>/dev/null | grep '*' | sed 's/* \(.*\)/ ${PARENCLR}(${BRANCHCLR}\1${PARENCLR}\)/'"

GIT_STATUS='$(branchname)'

Prompt_CHAR="\$"
PS1="\[$boldgreen\]\u\[$cyan\]::\[$boldred\]\h \[$cyan\]{\[$boldwhite\].../\W\[$cyan\]}\[$reset\]$GIT_STATUS\[$reset\]$Prompt_CHAR "

ここで設定した方法では、gitブランチの括弧は、gitブランチにいる場合にのみ表示され、それ以外の場合は空白になります。

1
Dan L

grawityの回答 に基づいて、以下はANSI制御シーケンスをASCII SOH^A)およびSTXで囲みます。 (^B)これはそれぞれ\[および\]と同等です:

function readline_ANSI_escape() {
  if [[ $# -ge 1 ]]; then
    echo "$*"
  else
    cat  # Read string from STDIN
  fi | \
  Perl -pe 's/(?:(?<!\x1)|(?<!\\\[))(\x1b\[[0-9;]*[mG])(?!\x2|\\\])/\x1\1\x2/g'
}

次のように使用します。

$ echo $'\e[0;1;31mRED' | readline_ANSI_escape

または:

$ readline_ANSI_escape "$string"

ボーナスとして、関数を複数回実行しても、すでにエスケープされている制御コードは再エスケープされません。

0
Tom Hale