Выровнять по праву часть подсказки

17927
Felix Andersen

Я уверен, что я видел кого-то, у кого часть его приглашения выровнена по правому краю в окне терминала, а затем фактический курсор начинался со второй строки. Я знаю, что могу получить вторую строку с "\ n" в PS1, но я не могу понять, как выровнять ее часть вправо. Было ли то, что я увидел, просто добавить пробел между двумя строками?

22

8 ответов на вопрос

25
nickl-

Основываясь на информации, которую я нашел здесь, я смог найти более простое решение для выравнивания по правому краю с учетом содержимого переменной длины справа или слева, включая поддержку цвета. Добавлено здесь для вашего удобства ...

Примечание по цветам: использование \033выхода в пользу альтернатив, без \[\]группировок, оказывается наиболее совместимым и поэтому рекомендуемым.

Хитрость заключается в том, чтобы сначала написать правую часть, а затем использовать возврат каретки ( \r) для возврата к началу строки и продолжить перезаписывать содержимое левой части поверх этого следующим образом:

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

Я использую tput colsв Mac OS X для получения ширины терминала / консоли, terminfoпоскольку моя $COLUMNSпеременная не заполнена, envно вы можете заменить заменяемое *значение " " %*s, указав " $", или любое другое значение, которое вы предпочитаете.

В следующем примере $RANDOMпоказано, как генерировать содержимое различной длины, включая цвета, и показано, как можно извлечь функции для рефакторинга реализации в повторно используемые функции.

function prompt_right() { echo -e "\033[0;36m$(echo $)\033[0m" }  function prompt_left() { echo -e "\033[0;35m$\033[0m" }  function prompt() { compensate=11 PS1=$(printf "%*s\r%s\n\$ " "$(($(tput cols)+$))" "$(prompt_right)" "$(prompt_left)") } PROMPT_COMMAND=prompt 

Поскольку printfпредполагается, что длина строки равна числу символов, которое нам необходимо компенсировать количеством символов, необходимых для визуализации цветов, вы всегда найдете его ниже конца экрана из-за непечатных символов ANSI без компенсации. Символы, необходимые для цвета, остаются постоянными, и вы обнаружите, что также printf учитывает изменение длины, как, например, возвращается $RANDOM', что сохраняет правильное выравнивание в такте.

Это не тот случай со специальной Баш строки управляющих последовательностями (т.е.. \u, \w, \h, \t), Хотя, как это будет только записывать длину 2, потому что баш будет переводить только их, когда появится подсказка, после Printf вынес строку. Это не влияет на левую сторону, но лучше избегать их справа.

Не имеет значения, если сгенерированный контент будет оставаться постоянной длины, хотя. Как с \tопцией времени, которая всегда будет отображать одинаковое количество символов (8) в течение 24 раз. Нам нужно только учесть компенсацию, необходимую для учета разницы между подсчитанными 2 символами, которая в этих случаях приводит к 8 символам при печати.

Имейте в виду, что вам может потребоваться тройной экранирование \\\некоторых escape-последовательностей, которые в противном случае содержат значение для строк Как и в следующем примере, текущий экранирование рабочего каталога \wне имеет никакого значения в противном случае, поэтому он работает, как и ожидалось, но время \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)+$))" "$(prompt_right)" "$(prompt_left)") } PROMPT_COMMAND=prompt 

NJoy!

16
Gilles

То, что вы хотите, довольно легко сделать, отобразив первую строку перед отображением подсказки. Например, ниже показано приглашение \wслева от первой строки и приглашение \u@\hсправа от первой строки. Он использует $COLUMNSпеременную, которая содержит ширину терминала и $PROMPT_COMMANDпараметр, который оценивается до того, как bash отобразит приглашение.

print_pre_prompt ()  {  PS1L=$PWD if [[ $PS1L/ = "$HOME"/* ]]; then PS1L=\~$; fi PS1R=$USER@$HOSTNAME printf "%s%$(($COLUMNS-${#PS1L}))s" "$PS1L" "$PS1R" } PROMPT_COMMAND=print_pre_prompt 
Обратите внимание, что все становится значительно сложнее, если вы хотите цветную подсказку слева, так как непечатные символы означают, что длина строки не равна количеству отображаемых символов. Mu Mind 12 лет назад 2
И этот, и ответ с наибольшим количеством голосов не будут работать правильно, если `.inputrc` имеет [` установить show-mode-in-prompt on`] (https://cnswww.cns.cwru.edu/php/chet/readline/ readline.html # SEC10). Оба не рассчитывают длину непечатаемых [кодов ANSI CSI] (https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes) и неправильно заключают их в `\ [` и `\ ] `как упомянуто @Mu Mind. См. [Этот ответ] (https://superuser.com/a/1203400/365890) для решения. Tom Hale 7 лет назад 1
7
jmervine

Использование printfс $COLUMNSработал очень хорошо, что-то вроде:

printf "%$s\n" "hello" 

Это правильно оправдало это для меня.

5
Tom Hale

Следующее поместит текущую дату и время в RED на 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="\[$\e[$C\e[${#PS1RHS_stripped}D$$\]$" 

Преимущества:

  • Работает правильно с цветами и кодами ANSI CSI в приглашении RHS
  • Нет подпроцессов. shellcheckчистый.
  • Работает правильно, если .inputrcимеет set show-mode-in-prompt on.
  • Правильно инкапсулирует несрочную длину, давая символы \[и \]так, что редактирование текст, введенный в командной строке не вызывает подсказку перепечатать странно.

Примечание . Вам необходимо убедиться, что все последовательности цветов в $PS1до выполнения этого кода правильно заключены в них \[и \]их вложенность отсутствует.

хотя мне нравится этот подход в теории, на практике он не работает "из коробки" (ubuntu 18.04, GNU bash 4.4.19): сначала добавление кода непосредственно в .bashrc выдает ошибку `bash: local: only only используется в функции`, которую тривиально исправить, и после этого он ничего не показывает, потому что `COLUMNS` не определен: его нужно заменить на` $ (tput cols) `. тот же результат, если фрагмент сохраняется в другом файле, а затем помещается в `.bashrc`. Polentino 6 лет назад 0
Спасибо @Polentino. Я обновил код для запуска `tput cols`, если` $ COLUMNS` не установлен. И да, этот код должен быть внутри функции. Я использую `PROMPT_COMMAND = '_ prompt_bash_set'` и называю функцию` _prompt_bash_set`. Tom Hale 6 лет назад 1
2
dylnmc

I just thought I would throw mine in here. It's almost exactly the same as the GRML zsh prompt (except zsh updates it's prompt a little better on new lines and back spaces - which is impossible to replicate in bash ... well very difficult at this point in time, at least).

I spent a good three days on this (only tested on a laptop running arch), so here's a screenshot and then the stuff that goes in my ~/.bashrc :)

screenshot of bash prompt in action

warning - it's a little crazy

important aside - every ^[ (such as ^[[34m) is really the escape character (char)27. The only way I know how to insert this is to enter ctrl+([v) (i.e. hit both [ and v while ctrl is held down.

# 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" "$" "^[[35m" "^[[39m" fi; } # grml PS1 string PS1="\n\[\e[F\e[0m\]\$(_PS1_ret)\[\e[34;1m\]$\u\[\e[0m\]@\h \[\e[01m\]\w\$(_PS1_git) \[\e[0m\]% " 

I'm still working on making the colors configurable, but I am happy with the colors as they are now.


Currently working on a fix for the crazy ^[ character and easy color switching :)

Это не Ctrl + [и v одновременно, это Ctrl + v, а затем Ctrl + [. NieDzejkob 6 лет назад 0
0
Dennis Williamson

Вы можете использовать printfдля правильного выравнивания:

$ printf "%10s\n" "hello" hello  $ PS1='$(printf "%10s" "$somevar")\w\$ ' 
0
Mu Mind

Добавляя ответ Джайлза, я написал что-то, чтобы лучше обрабатывать цвета (при условии, что они правильно заключены в \[и \]. Это индивидуально для каждого случая и не обрабатывает каждый случай, но позволяет мне настроить 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_exp="$" SHORT_PWD=$ PS1L_exp="$" PS1L_clean="$(sed -r 's:\\\[([^\\]|\\[^]])*\\\]::g' <<<$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='$\[\033[01;32m\]\u@\h\[\033[00m\]' PS1='\[\033[01;34m\]\w\[\033[00m\]\$ ' PROMPT_COMMAND=print_pre_prompt 

Примечание: я также добавил новую строку после PS1R, которая не имеет визуальной разницы, но, похоже, не дает подсказке искажаться, если вы прокручиваете назад некоторые команды в своей истории команд.

Я уверен, что кто-то еще может улучшить это, и, возможно, обобщить некоторые из особых случаев.

0
Daniel Da Cunha

Here is a solution based on PROMPT_COMMAND and 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" 

The magic is performed by:

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

Which is broken down by:

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 

In PS1, tput is escaped with \[ \] so that it is not counted in displayed length.

Похожие вопросы