提示的右对齐部分


27

我确信我已经看到有人在其终端窗口中将其提示的一部分与右侧对齐,然后将实际光标从第二行开始。我知道我可以在PS1中使用“ \ n”来实现第二行,但是我无法弄清楚如何将其中的一部分对齐到右侧。我看到的只是在两个字符串之间添加了空格吗?

Answers:


17

您可以通过在显示提示之前显示第一行来轻松完成所需的操作。例如,以下内容\w在第一行的左侧显示提示,在第一行\u@\h的右侧显示提示。它利用$COLUMNS包含终端宽度的变量和$PROMPT_COMMAND在bash显示提示之前评估的参数。

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
请注意,如果要使用彩色的左提示,事情会变得更加复杂,因为非打印字符意味着字符串长度与显示的字符数不同。
Mu Mind

1
这两者和最高投票的答案,如果不能正常工作.inputrcset 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)不过,因为这些只会记录2的长度,因为bash将只显示提示时,翻译它们的printf已经呈现后弦。这不会影响左侧,但最好避免右侧。

但是,如果生成的内容将保持恒定的长度,则没有关系。与time \t选项一样,它将始终在24时间内渲染相同数量的字符(8)。在这种情况下,我们只需要考虑补偿所需要的补偿,以适应计算出的2个字符之间的差异(打印时得出8个字符)。

请记住,您可能需要对\\\某些转义序列进行三倍转义,否则这些转义序列将保留字符串的含义。与下面的示例一样,当前的工作目录转义符\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

欢乐!


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
Polentino

1
谢谢@Polentino。tput cols如果$COLUMNS未设置,我已经更新了要运行的代码。是的,此代码应在函数内部。我使用PROMPT_COMMAND='_prompt_bash_set'并命名该函数_prompt_bash_set
汤姆·黑尔

2

我只是以为我会把我扔在这里。它与GRML zsh提示符几乎完全相同(除了zsh更新,它的提示符在新行和退格处要好一点-不可能在bash中复制...至少在目前是非常困难的)。

我花了三天时间(仅在运行arch的笔记本电脑上进行了测试),所以这是屏幕截图,然后是〜/ .bashrc中的内容:)

bash提示的屏幕截图

警告 -这有点疯狂

除了重要的一点 -每个^[(诸如^[[34m)都是逃脱字符(char)27。我知道如何插入的唯一方法是输入ctrl+([v)(即同时按下[并按住不放)。vctrl

# 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

加上Giles的答案,我写了一些东西来更好地处理颜色(前提是颜色正确地封装在\[和中\]。这是逐个案的,并且不能处理所有情况,但是它使我可以将PS1L设置为与PS1相同的语法并将(无色)日期用作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 / dotfiles / 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以\ [\]进行转义,因此不计入显示的长度。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.