Право вирівняйте частину підказки


27

Я впевнений, що я бачив, що хтось частину їх підказки вирівнюється праворуч у вікні терміналу, а потім запускає фактичний курсор у другому рядку. Я знаю, що я можу досягти другого рядка з "\ n" в PS1, але не можу зрозуміти, як вирівняти частину його праворуч. Чи було додано те, що я бачив просто пробіли між двома рядками?

Відповіді:


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
І ця, і найвища відповідь не відповідають правильності, якщо .inputrcє set show-mode-in-prompt on. Обидва не обчислюють довжину недосяжних кодів ANSI CSI і не належним чином вкладають їх у \[та, \]як згадує @Mu Mind. Дивіться цю відповідь для резолюції.
Том Хейл

26

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

Примітка щодо кольорів: використання \033втечі на користь альтернатив, без \[\]групувань, виявляється найбільш сумісним і рекомендованим для цього.

Трюк полягає в тому, щоб спочатку написати праву частину, а потім скористатися return carriage ( \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наприклад, ', що зберігає наше правильне вирівнювання в такті.

Це не той випадок зі спеціальною Баш рядки керуючих послідовностями (тобто .. \u, \w, \h, \t) , Хоча, як це буде тільки записувати довжину 2 , тому що баш буде переводити тільки їх , коли з'явиться підказка, після Printf виніс рядок. Це не стосується лівої сторони, але краще уникати їх праворуч.

Немає наслідків, якщо створений вміст залишатиметься незмінним. Як і з \tваріантом часу, який завжди відображатиме однакову кількість символів (8) протягом 24 разів. Нам потрібно лише врахувати компенсацію, необхідну для врахування різниці між двома символами, що підраховуються, що призводить до 8 символів при друкуванні в цих випадках.

Майте на увазі, що вам може знадобитися втричі вийти з \\\деяких послідовностей евакуації, які інакше мають значення для рядків. Як і в наступному прикладі, поточне втечу робочого каталогу \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)+${compensate}))" "$(prompt_right)" "$(prompt_left)")
}
PROMPT_COMMAND=prompt

nJoy!


8

Використання printfз $COLUMNSдуже добре працювали, щось на кшталт:

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

Це правильно виправдало це ідеально для мене.


6

Далі буде розміщено поточну дату та час у 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="\[${Save}\e[${COLUMNS:-$(tput cols)}C\e[${#PS1RHS_stripped}D${PS1RHS}${Rest}\]${PS1}"

Переваги:

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

Примітка : Ви повинні гарантувати , що будь-які кольорові послідовності в $PS1до цього коду exeucted правильно укладені в \[і \]й що немає вкладеності з них.


в той час як мені подобається такий підхід теоретично, на практиці він не виходить з поля (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 ... ну дуже важко в цей момент часу).

Я витратив на це добрі три дні (протестував лише на ноутбуці під керуванням арки), тож ось скріншот, а потім те, що йде в моєму ~ / .bashrc :)

скріншот баш-підказки в дії

попередження - це трохи шалено

Важливо убік - кожен ^[(такий як ^[[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

Додаючи відповідь Гілза, я написав щось, щоб краще обробити кольори (за умови, що вони належним чином укладені \[і \]. Це в кожному конкретному випадку і не обробляє кожен випадок, але це дозволяє мені встановити 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виводиться з \ [\] так, щоб він не враховувався у відображувану довжину.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.