Bash: отображение статуса выхода в приглашении:


11
GREEN="\e[1;32m"
RED="\e[1;31m"
NONE="\e[m"

get_exit_status(){
   es=$?
   if [ $es -eq 0 ]
   then
       echo -e "${GREEN}${es}${NONE}"
   else
       echo -e "${RED}${es}${NONE}"
   fi
}

get_path(){
    #dummy function
    echo "PATH"
}

PROMPT_COMMAND='exitStatus=$(get_exit_status)'

Следующее дает мне правильный exitStatus, но цветовые переменные не раскрываются:

PS1='${RED}\h $(get_path) ${exitStatus}${NONE} '

Тем не менее, приведенный ниже, дает мне цвета, но состояние выхода не обновляется:

PS1="${RED}\h $(get_path) ${exitStatus}${NONE} "

Как правильно это сделать? Как я могу это исправить, чтобы оба exitStatus и цвета работали?

Ответы:


8

Жиль определил вашу главную проблему, но я хотел попытаться объяснить это по-другому.

Bash интерпретирует экранирование специальных приглашений только перед раскрытием любых переменных в приглашении. Это означает, что использование \eпеременной, развернутой из приглашения, не работает, даже если она работает непосредственно в PS1.

Например, это работает как ожидалось и дает красный текст:

PS1='\e[1;31m this is in red '

Но это не так, он просто помещает литерал \eв приглашение:

RED='\e[1;31m'
PS1="$RED not in red "

Если вы хотите сохранить экранирование цвета в переменных, вы можете использовать ANSI-C quoting ( $'...'), чтобы поместить буквенный escape-символ в переменную.

Чтобы сделать это, вы можете изменить свое определение GREEN, REDи NONE, таким образом , их значение является фактической последовательностью выхода.

GREEN=$'\033[1;32m'
RED=$'\033[1;31m'
NONE=$'\033[m'

Если вы сделаете это, ваш первый PS1с одинарными кавычками должен работать:

PS1='${RED}\h $(get_path) ${exitStatus}${NONE} '

Однако тогда у вас возникнет вторая проблема.

Попробуйте запустить это, затем нажмите Up Arrow, затем Home, и ваш курсор не вернется к началу строки.

Чтобы исправить это, измените PS1на включение \[и \]вокруг цветовых escape-последовательностей, например

PS1='\[${RED}\]\h $(get_path) $?\[${NONE}\] '

Вы не можете использовать get_exit_statusздесь должным образом, так как его вывод содержит как печатные (код выхода), так и непечатаемые символы (цветовые коды), и нет возможности правильно пометить его в приглашении. Помещение \[...\]будет помечено как непечатное полностью, что не правильно. Вам нужно изменить функцию, чтобы она печатала только правильный цветовой код, а затем заключить его \[...\]в подсказку.


\[есть \1и \[есть \2. Это соответствует некоторой RL_PROMPT_{START,END}_IGNOREвещи readline , которая просит ее игнорировать байты при подсчете длины подсказки на экране. См. Lists.gnu.org/archive/html/bug-bash/2015-08/msg00027.html .
Arthur2e5

@ Arthur2e5 ли вы имеете в виду \]это \2? И ты имеешь ввиду, для чего это нужно ${exitStatus}? Моя точка зрения заключалась в том, что ${exitStatus}она не содержит непечатаемых символов, поэтому bash должен уметь правильно определять, на сколько символов он перемещает подсказку без \[и \]в \[${exitStatus}\].
Микель

Проблема в том, что он содержит некоторые цвета. (ANSI
Escape

@ Arthur2e5 Э-э, я полностью пропустил это. :) Зачем тебе красить ... не бери в голову. :)
Микель

1
«Bash эффективно вызывает эхо на вашей PS1, а не echo -e» - ну, это неправильно или просто упускает суть. Bash действительно расширяет обратную косую черту как \eи \033\[/ \],, \uи \h) из приглашения, он просто делает это перед раскрытием переменных. Так PS1='\e[1;31m red'работает, red='\e[1;31m'; PS1='$red red'нет.
ilkkachu

3

Когда вы запускаете PS1='${RED}\h $(get_path) ${exitStatus}${NONE} ', PS1переменная устанавливается на ${RED}\h $(get_path) ${exitStatus}${NONE}, где только \hпоследовательность подсказок. После того, как последовательности подсказок расширены (дают ${RED}darkstar $(get_path) ${exitStatus}${NONE}), оболочка выполняет обычные расширения, такие как переменные. Вы получите отображаемое приглашение, которое выглядит следующим образом \e[1;31mdarkstar PATH 0\e[m. Ничто на этом пути не расширяет \eпоследовательности до реальных escape-символов.

При запуске PS1="${RED}\h $(get_path) ${exitStatus}${NONE} ", то PS1переменная устанавливается \e[1;31m\h PATH 0\e[m. Переменные RED, exitStatusи NONEраскрываются во время назначения. Тогда подсказка содержит три оперативные управляющие последовательности ( \e, \hи \eснова). На этом этапе нет переменных оболочки для расширения.

Для того, чтобы увидеть цвета, вам нужно, чтобы переменные цвета содержали реальные escape-символы. Вы можете сделать это следующим образом:

RED=$'\033[1;31m'
NONE=$'\033[m'
PS1='\[${RED}\]\h \w $?\[${NONE}\] '

$'…'Расширяет восьмеричные последовательности с обратной косой чертой и некоторые последовательности букв с обратной косой чертой, такие как \n, но не включая \e. Я сделал три других изменения в вашем приглашении:

  • Используйте \[…\]вокруг непечатаемых последовательностей, таких как команды изменения цвета. В противном случае ваш дисплей будет искажен, потому что bash не может определить ширину приглашения.
  • \w является встроенной escape-последовательностью для печати текущего каталога
  • Вам не нужно ничего сложного, чтобы показать $?в подсказке, если у вас нет PROMPT_COMMANDв первую очередь.

Я думаю, что идея состояла в том, чтобы сделать подсказку зеленой при успехе и красной при неудаче.
Mattdm

Да, PS1это неправильно, но совет использования $'...'для REDи GREENдолжны сделать его работу с помощью кендырь - х PS1.
Микель

1

Пытаться:

PS1='`exitStatus=$?;if [ $exitStatus -eq 0 ];then echo "\['${GREEN}'\]";else echo "\['${RED}'\]";fi;echo "\h $(get_path) ${exitStatus}${NONE}"`'

1
Спасибо, это работает, но есть ли способ сделать это, не вставляя оператор if в приглашение?
Dogbane

1

Вот подход, который я использовал, он избегает использования PROMPT_COMMAND.

# This function is called from a subshell in $PS1,
# to provide a colourised visual indicator of the exit status of the last run command
__COLOURISE_EXIT_STATUS() {
    # uncomment the next line for exit code output after each command, useful for debugging and testing
    #printf -- "\nexit code: $1\n" >&2
    [[ 0 == "$1" || 130 == "$1" ]] && printf -- "$GREEN" || printf -- "$RED"
}

Тогда мой $PS1следующий:

PS1='# ${debian_chroot:+($debian_chroot)}'"${GREEN}\u${YELLOW}@${DARK_YELLOW}\h${WHITE}:${LIGHT_BLUE}\w${WHITE}\n"'\[$(__COLOURISE_EXIT_STATUS $?)\]# \$'"\[${WHITE}\] "

1
Хотя это не имеет значения в данном конкретном случае, поскольку единственное значение $?может иметь целое число, вам действительно следует использовать printf '%b' "$GREEN"вместо этого. Кроме того, избегайте использования имен функций с префиксом __или _как они используются bash-complete.
nyuszika7h

1

Здесь вы идете - Это работает для меня (TM) в Ubuntu и других Linux (Linuxen?).

Причиной включения обнаружения кода выхода $PS1является то, что один хост имеет доступ только для чтения $PROMPT_COMMANDдо чтения .bashrc.


0

Ведь PROMPT_COMMANDчище определить функцию и использовать это:

prompt_command() {
    # ...
}
PROMPT_COMMAND=prompt_command
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.