プロンプトの一部を右揃え


27

誰かが端末ウィンドウの右側にプロンプ​​トの一部を揃えて、実際のカーソルを2行目から開始しているのを見たことがあると思います。PS1の「\ n」で2行目を達成できることは知っていますが、その一部を右に揃える方法がわかりません。2つの文字列の間に空白が追加されたのを見ましたか?

回答:


17

プロンプトを表示する前に最初の行を表示することで、必要なことをかなり簡単に実行できます。たとえば、次の例で\wは、最初の行の左側にプロンプトが表示され、最初の行\u@\hの右側にプロンプトが表示されます。これは、使用可能$COLUMNS端末の幅と含まれている変数$PROMPT_COMMANDbashのプロンプトを表示する前に評価されたパラメータを。

print_pre_prompt () 
{ 
    PS1L=$PWD
    if [[ $PS1L/ = "$HOME"/* ]]; then PS1L=\~${PS1L#$HOME}; fi
    PS1R=$USER@$HOSTNAME
    printf "%s%$(($COLUMNS-${#PS1L}))s" "$PS1L" "$PS1R"
}
PROMPT_COMMAND=print_pre_prompt

3
印刷されない文字は、文字列の長さが表示される文字の数と同じではないことを意味するため、色付きの左側のプロンプトが必要な場合、事態は非常に複雑になることに注意してください。
ムーマインド

1
.inputrc持ってset show-mode-in-prompt onいる場合、これと最高の投票された答えの両方が正しく動作しません。どちらも、非prinableの長さを計算していないANSI CSIコードを、そして適切にそれらを囲まない\[\]@Muマインドで述べたように。解決策については、この回答を参照してください。
トム・ヘイル

26

ここで見つけた情報に基づいて、色のサポートを含む左右の可変長コンテンツに対応しながら、右揃えの簡単なソリューションを発見することができました。あなたの便宜のためにここに追加しました...

色に関する注意:グループ化\033せずに、代替を支持してエスケープを使用する\[\]と、最も互換性があり、したがって推奨されます。

トリックは、最初に右側を記述し、次にキャリッジリターン(\r)を使用して行の先頭に戻り、次のようにその左側のコンテンツを上書きし続けることです。

prompt() {
    PS1=$(printf "%*s\r%s\n\$ " "$(tput cols)" 'right' 'left')
}
PROMPT_COMMAND=prompt

私が使用していますtput colsから、端末/コンソール幅を取得するためにはMac OS X上でterminfo私のため、$COLUMNSVARはに移入されていないenvが、あなたは、交換可能な「置換してもよい*」の値を%*s提供することで、「${COLUMNS}その代わり、」、またはあなたが好む他の値。

次の例では$RANDOM、色を含むさまざまな長さのコンテンツを生成するために使用し、関数を抽出して実装を再利用可能な関数にリファクタリングする方法を示します。

function prompt_right() {
  echo -e "\033[0;36m$(echo ${RANDOM})\033[0m"
}

function prompt_left() {
  echo -e "\033[0;35m${RANDOM}\033[0m"
}

function prompt() {
    compensate=11
    PS1=$(printf "%*s\r%s\n\$ " "$(($(tput cols)+${compensate}))" "$(prompt_right)" "$(prompt_left)")
}
PROMPT_COMMAND=prompt

以来printf、私たちは色をレンダリングするために必要な文字の量を補うために必要な文字の#する文字列の長さを想定している非の補償なしANSI文字が印刷されているため、あなたは常に短い画面の最後のそれを見つけるでしょう。色に必要な文字は一定のままであり、printfは$RANDOM、たとえば ' によって返される長さの変化も考慮に入れることがわかります。

これは、(すなわち。特別なbashのプロンプトのエスケープシーケンスの場合ではありません\u\w\h\tプロンプトが表示されたときのprintfは、文字列をレンダリングした後にbashは唯一、それらを翻訳しますので、これらは唯一の2の長さを記録するなど、)けれども。これは左側には影響しませんが、右側では避けるのが最善です。

ただし、生成されたコンテンツが一定の長さのままである場合、結果はありません。\t24時間にわたって常に同じ量の文字(8)をレンダリングするtime オプションと同様です。これらの場合、印刷されると8文字になる、カウントされた2文字の差に対応するために必要な補償を考慮するだけです。

\\\文字列の意味を保持するエスケープシーケンスをトリプルエスケープする必要がある場合があることに注意してください。次の例のように、現在の作業ディレクトリのエスケープ\wは意味を持たないため、期待どおりに動作しますが、time \tはタブ文字を意味し、最初にトリプルエスケープしないと期待どおりに動作しません。

function prompt_right() {
  echo -e "\033[0;36m\\\t\033[0m"
}

function prompt_left() {
  echo -e "\033[0;35m\w\033[0m"
}

function prompt() {
    compensate=5
    PS1=$(printf "%*s\r%s\n\$ " "$(($(tput cols)+${compensate}))" "$(prompt_right)" "$(prompt_left)")
}
PROMPT_COMMAND=prompt

nJoy!


8

printfwithの使用$COLUMNSは非常にうまく機能しました。

printf "%${COLUMNS}s\n" "hello"

それは私にとって完璧に正当化されました。


6

以下は、端末のRHSの現在の日付と時刻を赤で表示します。

# Create a string like:  "[ Apr 25 16:06 ]" with time in RED.
printf -v PS1RHS "\e[0m[ \e[0;1;31m%(%b %d %H:%M)T \e[0m]" -1 # -1 is current time

# Strip ANSI commands before counting length
# From: https://www.commandlinefu.com/commands/view/12043/remove-color-special-escape-ansi-codes-from-text-with-sed
PS1RHS_stripped=$(sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <<<"$PS1RHS")

# Reference: https://en.wikipedia.org/wiki/ANSI_escape_code
local Save='\e[s' # Save cursor position
local Rest='\e[u' # Restore cursor to save point

# Save cursor position, jump to right hand edge, then go left N columns where
# N is the length of the printable RHS string. Print the RHS string, then
# return to the saved position and print the LHS prompt.

# Note: "\[" and "\]" are used so that bash can calculate the number of
# printed characters so that the prompt doesn't do strange things when
# editing the entered text.

PS1="\[${Save}\e[${COLUMNS:-$(tput cols)}C\e[${#PS1RHS_stripped}D${PS1RHS}${Rest}\]${PS1}"

利点:

  • RHSプロンプトの色とANSI CSIコードで正しく動作します
  • サブプロセスはありません。shellcheckクリーン。
  • があれば正しく動作し.inputrcますset show-mode-in-prompt on
  • 正確には非プロンプトレングス与える文字をカプセル化\[し、\]プロンプトに入力したテキストを編集すると、妙に転載するプロンプトを起こさないように、。

$PS1このコードが実行される前の色シーケンスが適切に囲まれ\[\]ネストされていないことを確認する必要があります。


私は理論的にはこのアプローチが好きですが、実際にはすぐに動作しません(ubuntu 18.04、GNU bash 4.4.19):コードを直接.bashrcに直接追加するとエラーが発生しますがbash: local: can only be used in a function、これは修正するのは簡単ですその後、COLUMNS定義されていないため、何も表示されません:で置き換える必要があり$(tput cols)ます。スニペットが別のファイルに保存されてからにソースされた場合も同じ結果になります.bashrc
ポレンティーノ

1
@Polentinoに感謝します。が未設定のtput cols場合に実行するコードを更新しました$COLUMNS。はい、このコードは関数内にある必要があります。PROMPT_COMMAND='_prompt_bash_set'function を使用して名前を付けます_prompt_bash_set
トム・ヘイル

2

私はここに私のものを投げると思った。GRML zshプロンプトとほぼ同じです(zshの更新を除くと、新しい行とバックスペースでプロンプトが少し良くなります-これはbashで複製することはできません...少なくとも現時点では非常に困難です)。

私はこれにかなりの3日間を費やしました(archを実行しているラップトップでのみテストされています)ので、ここにスクリーンショットがあり、次に〜/ .bashrcにあるものがあります:)

アクションのbashプロンプトのスクリーンショット

警告 -それは少しクレイジーです

重要なのは、すべて^[(など^[[34m)が本当にエスケープ文字であるということです(char)27。私はこれを挿入する方法を知っている唯一の方法は、入力することであるctrl(+ [v)(すなわち両方にヒット[してvいる間は、ctrl押されています。

# grml battery?
GRML_DISPLAY_BATTERY=1

# battery dir
if [ -d /sys/class/power_supply/BAT0 ]; then
    _PS1_bat_dir='BAT0';
else
    _PS1_bat_dir='BAT1';
fi

# ps1 return and battery
_PS1_ret(){
    # should be at beg of line (otherwise more complex stuff needed)
    RET=$?;

    # battery
    if [[ "$GRML_DISPLAY_BATTERY" == "1" ]]; then
        if [ -d /sys/class/power_supply/$_PS1_bat_dir ]; then
            # linux
            STATUS="$( cat /sys/class/power_supply/$_PS1_bat_dir/status )";
            if [ "$STATUS" = "Discharging" ]; then
                bat=$( printf ' v%d%%' "$( cat /sys/class/power_supply/$_PS1_bat_dir/capacity )" );
            elif [ "$STATUS" = "Charging" ]; then
                bat=$( printf ' ^%d%%' "$( cat /sys/class/power_supply/$_PS1_bat_dir/capacity )" );
            elif [ "$STATUS" = "Full" ] || [ "$STATUS" = "Unknown" ] && [ "$(cat /sys/class/power_supply/$_PS1_bat_dir/capacity)" -gt "98" ]; then
                bat=$( printf ' =%d%%' "$( cat /sys/class/power_supply/$_PS1_bat_dir/capacity )" );
            else
                bat=$( printf ' ?%d%%' "$( cat /sys/class/power_supply/$_PS1_bat_dir/capacity )" );
            fi;
        fi
    fi

    if [[ "$RET" -ne "0" ]]; then
        printf '\001%*s%s\r%s\002%s ' "$(tput cols)" ":( $bat " "^[[0;31;1m" "$RET"
    else
        printf '\001%*s%s\r\002' "$(tput cols)" "$bat "
    fi;
}

_HAS_GIT=$( type 'git' &> /dev/null );

# ps1 git branch
_PS1_git(){
    if ! $_HAS_GIT; then
        return 1;
    fi;
    if [ ! "$( git rev-parse --is-inside-git-dir 2> /dev/null )" ]; then
        return 2;
    fi
    branch="$( git symbolic-ref --short -q HEAD 2> /dev/null )"

    if [ "$branch" ]; then
        printf ' \001%s\002(\001%s\002git\001%s\002)\001%s\002-\001%s\002[\001%s\002%s\001%s\002]\001%s\002' "^[[0;35m" "^[[39m" "^[[35m" "^[[39m" "^[[35m" "^[[32m" "${branch}" "^[[35m" "^[[39m"
    fi;
}

# grml PS1 string
PS1="\n\[\e[F\e[0m\]\$(_PS1_ret)\[\e[34;1m\]${debian_chroot:+($debian_chroot)}\u\[\e[0m\]@\h \[\e[01m\]\w\$(_PS1_git) \[\e[0m\]% "

私はまだ色を設定可能にすることに取り組んでいますが、現在の色に満足しています。


現在、クレイジーな^[キャラクターと簡単な色の切り替えの修正に取り組んでいます:)


Ctrl + [とvが同時にではなく、Ctrl + vの後にCtrl + [が続きます。
-NieDzejkob


0

ジャイルズの答えに加えて、色をより適切に処理するために何かを書いた(それらが適切に囲まれ\[\]いる場合、それはケースバイケースであり、すべてのケースを処理しませんが、PS1と同じ構文でPS1Lを設定できますそして、(色付けされていない)日付をPS1Rとして使用します。

function title {
    case "$TERM" in
    xterm*|rxvt*)
        echo -en "\033]2;$1\007"
        ;;
    *)
        ;;
    esac
}

print_pre_prompt() {
    PS1R=$(date)
    PS1L_exp="${PS1L//\\u/$USER}"
    PS1L_exp="${PS1L_exp//\\h/$HOSTNAME}"
    SHORT_PWD=${PWD/$HOME/~}
    PS1L_exp="${PS1L_exp//\\w/$SHORT_PWD}"
    PS1L_clean="$(sed -r 's:\\\[([^\\]|\\[^]])*\\\]::g' <<<$PS1L_exp)"
    PS1L_exp=${PS1L_exp//\\\[/}
    PS1L_exp=${PS1L_exp//\\\]/}
    PS1L_exp=$(eval echo '"'$PS1L_exp'"')
    PS1L_clean=$(eval echo -e $PS1L_clean)
    title $PS1L_clean
    printf "%b%$(($COLUMNS-${#PS1L_clean}))b\n" "$PS1L_exp" "$PS1R"
}

ここでは、githubの上にある:dbarnett /ドットファイル/ right_prompt.sh。.bashrcで次のように使用します。

source $HOME/dotfiles/right_prompt.sh
PS1L='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]'
PS1='\[\033[01;34m\]\w\[\033[00m\]\$ '
PROMPT_COMMAND=print_pre_prompt

注:PS1Rの後に改行も追加しましたが、視覚的な違いはありませんが、コマンド履歴で特定のコマンドをスクロールバックすると、プロンプトが文字化けするのを防ぐようです。

他の誰かがこれを改善し、おそらく特殊なケースのいくつかを一般化できると確信しています。


0

PROMPT_COMMANDとに基づくソリューションはtput次のとおりです。

function __prompt_command() {
  local EXIT="$?"             # This needs to be first
  history -a
  local COL=$(expr `tput cols` - 8)
    PS1="💻 \[$(tput setaf 196)\][\[$(tput setaf 21)\]\W\[$(tput setaf 196)\]]\[$(tput setaf 190)\]"
    local DATE=$(date "+%H:%M:%S")
  if [ $EXIT != 0 ]; then
    PS1+="\[$(tput setaf 196)\]\$"      # Add red if exit code non 0
    tput sc;tput cuu1; tput cuf $COL;echo "$(tput setaf 196)$DATE"; tput rc
  else
  PS1+="\[$(tput setaf 118)\]\$"
    tput sc;tput cuu1; tput cuf $COL;echo "$(tput setaf 118)$DATE"; tput rc
  fi
  PS1+="\[$(tput setaf 255)\] "
}
PROMPT_COMMAND="__prompt_command"

魔法は以下によって実行されます:

tput sc;tput cuu1; tput cuf $COL;echo "$(tput setaf 196)$DATE"; tput rc

内訳は次のとおりです。

tput sc                       # saved the cursor position
tput cuu1                     # up one line
tput cuf $COL                 # move $COL characters left
echo "$(tput setaf 196)$DATE" # set the colour and print the date
tput rc                       # restore the cursor position

PS1では、tput\ [\]でエスケープされているため、表示される長さにカウントされません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.