Bash: Wyświetl status wyjścia w trybie zachęty:


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)'

Poniżej podano poprawny parametr exitStatus, ale zmienne kolorów nie są rozwijane:

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

Jednak ten poniżej podaje kolory, ale status wyjścia nie jest aktualizowany:

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

Jak to zrobić? Jak mogę to naprawić, aby zarówno exitStatus, jak i kolory działały?

Odpowiedzi:


8

Gilles zidentyfikował twój główny problem, ale chciałem spróbować wyjaśnić go inaczej.

Bash interpretuje specjalne znaki zachęty tylko przed rozwinięciem jakichkolwiek zmiennych w znaku zachęty. Oznacza to, że użycie \ew zmiennej rozwiniętej z monitu nie działa, nawet jeśli działa bezpośrednio w PS1.

Na przykład działa to zgodnie z oczekiwaniami i daje czerwony tekst:

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

Ale tak nie jest, po prostu umieszcza literał \ew znaku zachęty:

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

Jeśli chcesz przechowywać $'...'znaki zmiany koloru w zmiennych, możesz użyć ANSI-C quoting ( ), aby wstawić dosłowny znak zmiany znaczenia w zmiennej.

Aby to zrobić, można zmienić definicję GREEN, REDi NONE, więc ich wartość jest rzeczywista sekwencja ucieczki.

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

Jeśli to zrobisz, Twoje pierwsze PS1z pojedynczymi cudzysłowami powinny działać:

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

Będziesz miał jednak drugi problem.

Spróbuj uruchomić to, a następnie naciśnij Up Arrow, a następnie Home, a kursor nie wróci do początku linii.

Aby to naprawić, zmień, PS1aby uwzględnić sekwencje specjalne koloru \[i \]wokół nich, np

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

Nie można get_exit_statustutaj prawidłowo używać , ponieważ jego dane wyjściowe zawierają zarówno znaki drukujące (kod wyjścia), jak i znaki niedrukowalne (kody kolorów) i nie ma możliwości prawidłowego oznaczenia go w monicie. Umieszczenie \[...\]oznaczałoby go jako całkowicie niedrukowalny, co jest nieprawidłowe. Będziesz musiał zmienić funkcję, aby drukowała tylko odpowiedni kod koloru, a następnie otoczyła go \[...\]w wierszu polecenia.


\[jest \1i \[jest \2. Te, które odpowiadają jakiejś linii readline RL_PROMPT_{START,END}_IGNORE, która prosi ją o zignorowanie bajtów podczas liczenia długości pytania na ekranie. Zobacz lists.gnu.org/archive/html/bug-bash/2015-08/msg00027.html .
Arthur2e5

@ Arthur2e5 Czy masz na myśli \]to \2? Czy masz na myśli to, dlaczego jest to potrzebne ${exitStatus}? Chodzi mi o to, że ${exitStatus}nie zawiera znaki inne niż drukowanie, więc bash powinien być w stanie prawidłowo określić ile znaków porusza się monit bez \[i \]w \[${exitStatus}\].
Mikel

Problem w tym, że zawiera pewne - kolory. (ANSI Escapes)
Arthur2e5

@ Arthur2e5 Ew, całkowicie mi tego brakowało. :) Dlaczego miałbyś umieszczać kolory ... nieważne. :)
Mikel

1
„Bash skutecznie wywołuje echo na PS1, a nie echo -e” - cóż, to źle lub po prostu brakuje mu sensu. Bash rozszerza ukośniki odwrotne, takie jak \ei \033(i \[/ \], \ui \h) od pytania, po prostu robi to przed rozwinięciem zmiennych. Tak PS1='\e[1;31m red'działa, red='\e[1;31m'; PS1='$red red'nie.
ilkkachu

3

Po uruchomieniu PS1='${RED}\h $(get_path) ${exitStatus}${NONE} 'The PS1zmienna jest ustawiona ${RED}\h $(get_path) ${exitStatus}${NONE}, gdzie tylko \hjest szybka sekwencja ucieczki. Po rozwinięciu (generowaniu ${RED}darkstar $(get_path) ${exitStatus}${NONE}) sekwencji podpowiedzi powłoka wykonuje zwykłe rozszerzenia, takie jak rozszerzenia zmienne. Wyświetlany jest monit, który wygląda \e[1;31mdarkstar PATH 0\e[m. Nic po drodze nie rozszerza \esekwencji na rzeczywiste znaki ucieczki.

Po uruchomieniu PS1="${RED}\h $(get_path) ${exitStatus}${NONE} "The PS1zmienna jest ustawiona \e[1;31m\h PATH 0\e[m. Zmienne RED, exitStatusi NONEsą rozszerzone w czasie zadania. Następnie natychmiastowe wiersz zawiera trzy sekwencje sterująca ( \e, \hi \eponownie). Na tym etapie nie ma zmiennych powłoki.

Aby zobaczyć kolory, potrzebujesz zmiennych kolorów, które zawierają rzeczywiste znaki specjalne. Możesz to zrobić w ten sposób:

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

$'…'rozszerza sekwencje odwrotne odwrotne i niektóre sekwencje odwrotnych liter, takie jak \n, ale bez uwzględnienia \e. Wprowadziłem trzy inne zmiany w pytaniu:

  • Używaj w \[…\]pobliżu nie drukowanych sekwencji, takich jak polecenia zmieniające kolory. W przeciwnym razie ekran skończy się zniekształcony, ponieważ bash nie może określić szerokości monitu.
  • \w jest wbudowaną sekwencją zmiany znaczenia do wydrukowania bieżącego katalogu.
  • Nie potrzebujesz niczego skomplikowanego do wyświetlenia $?w monicie, jeśli nie masz PROMPT_COMMANDw ogóle .

Myślę, że chodziło o to, by podpowiedź była zielona na sukces, a czerwona na porażkę.
mattdm

Tak, PS1jest źle, ale wskazówka, z której należy $'...'skorzystać REDi GREENpowinna sprawić, że będzie działać przy użyciu dogbane PS1.
Mikel

1

Próbować:

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

1
Dzięki, to działa, ale czy jest jakiś sposób, aby to osiągnąć bez konieczności umieszczania instrukcji if w znaku zachęty?
dogbane

1

Oto podejście, które wybrałem, pozwala uniknąć użycia 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"
}

Zatem moje $PS1jest następujące:

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

1
Chociaż w tym konkretnym przypadku nie ma to znaczenia, ponieważ jedyną wartością, jaką $?może mieć, jest liczba całkowita, naprawdę powinieneś jej użyć printf '%b' "$GREEN". Unikaj także używania nazw funkcji z prefiksem __lub, _ponieważ są one używane przez bash-uzupełnianie.
nyuszika7h

1

Proszę bardzo - This Works For Me (TM) w Ubuntu i innych Linuxach (Linuxen?).

Powodem wprowadzenia wykrywania kodu wyjścia $PS1jest to, że jeden host ma $PROMPT_COMMANDustawiony tylko do odczytu przed odczytaniem .bashrc.


0

Ponieważ PROMPT_COMMANDłatwiej jest zdefiniować funkcję i użyć jej:

prompt_command() {
    # ...
}
PROMPT_COMMAND=prompt_command
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.