Bash-Eingabeaufforderung mit dem letzten Beendigungscode

70

Also habe ich versucht, per Bash-Eingabeaufforderung so anzupassen, dass es so aussieht

[feralin@localhost ~]$ _

mit Farben. Ich habe es geschafft, konstante Farben zu erhalten (jedes Mal, wenn ich die Eingabeaufforderung sehe, die gleichen Farben), aber ich möchte, dass der Benutzername ('feralin') rot statt grün angezeigt wird, wenn der letzte Befehl einen Exit-Status ungleich Null hatte. Ich hatte die Idee dass:

\e[1;33m[$(if [[ $? == 0  ]]; then echo "\e[0;31m"; else echo "\e[0;32m"; fi)\u\e[m@\e[1;34m\h \e[0;35m\W\e[1;33m]$ \e[m

Nach meinen Beobachtungen $(if ...; fi)scheint das jedoch einmal ausgewertet zu werden, wenn das ausgeführt .bashrcwird, und das Ergebnis wird für immer danach ersetzt. Dadurch wird der Name immer grün, auch wenn der letzte Beendigungscode ungleich Null ist (wie in, echo $?). Passiert das? Oder stimmt einfach etwas anderes mit meiner Eingabeaufforderung nicht? Lange Frage kurz, wie bekomme ich meine Aufforderung, den letzten Exit-Code zu verwenden?

Feralin
quelle
3
So etwas ist mir auch noch nie gelungen. Ich habe jedoch $ {? # 0} in die Eingabeaufforderung eingefügt, die den numerischen Beendigungsstatus genau dann ausgibt, wenn er nicht Null ist.
Wes Hardaker
3
Es funktioniert wie es ist. Sie haben gerade grün und rot umgekehrt.
n. 'Pronomen' m.
3
Schamloser Plug für prompt.gem, der eine erweiterbare Eingabeaufforderung bereitstellt, die sowohl den Exit-Code als auch die Dauer des vorherigen Befehls enthält.
dimo414
2
Betreff: "Einmal ausgewertet, wenn das ausgeführt .bashrcwird" - das bedeutet, dass Sie beim Zuweisen die falschen Anführungszeichen setzen PS1. Muss einfach sein, nicht doppelt. Wenn dies nicht hilft, werden Sie $?von etwas anderem zurückgesetzt, das ausgeführt wird, bevor Ihre Eingabeaufforderung gedruckt wird. set -xermöglicht das Aufspüren solcher Befehle.
Charles Duffy
@ CharlesDuffy danke für den Tipp! Ich merke es mir.
Feralin

Antworten:

118

Wenn Sie anfangen, an eine komplexe PS1 zu grenzen, sollten Sie die Verwendung in Betracht ziehen PROMPT_COMMAND.
Damit setzen Sie es auf eine Funktion und es wird nach jedem Befehl ausgeführt, um die Eingabeaufforderung zu generieren.

Sie könnten Folgendes in Ihrem versuchen ~/.bashrc

PROMPT_COMMAND=__prompt_command # Func to gen PS1 after CMDs

__prompt_command() {
    local EXIT="$?"             # This needs to be first
    PS1=""

    local RCol='\[\e[0m\]'

    local Red='\[\e[0;31m\]'
    local Gre='\[\e[0;32m\]'
    local BYel='\[\e[1;33m\]'
    local BBlu='\[\e[1;34m\]'
    local Pur='\[\e[0;35m\]'

    if [ $EXIT != 0 ]; then
        PS1+="${Red}\u${RCol}"      # Add red if exit code non 0
    else
        PS1+="${Gre}\u${RCol}"
    fi

    PS1+="${RCol}@${BBlu}\h ${Pur}\W${BYel}$ ${RCol}"
}

Dies sollte tun, was es klingt Linie, die Sie wollen. Schauen Sie sich die Subdatei von my bashrc an , wenn Sie alle Dinge sehen möchten, die ich mit meiner __prompt_commandFunktion mache .

zurückhaltend
quelle
Interessant. Ich wusste nichts über PROMPT_COMMAND. Ich werde es jetzt versuchen.
Feralin
Alles klar, es funktioniert! Ich habe es nur ein wenig geändert, um die [...] Trennzeichen vor dem "$" einzufügen. Davon abgesehen war es perfekt. Vielen Dank!
Feralin
2
Ich würde vorschlagen, einen Variablennamen mit mindestens einem Kleinbuchstaben EXITals Vorwärtskompatibilitätspraxis zu verwenden. Siehe pubs.opengroup.org/onlinepubs/009695399/basedefs/… , vierter Absatz - Der Namespace von All-Caps-Namen wird für Variablen verwendet, die für das System oder die Shell von Bedeutung sind. Das Festhalten an Kleinbuchstaben verhindert das Überschreiben einer Umgebungsvariablen oder einer in die Shell integrierten Variablen, wenn nur versucht wird, eine Shell-Variable zuzuweisen. Zugegeben, mit einer lokalen Erklärung, dass das Überschreiben nur vorübergehend ist, was es hier weniger problematisch als gewöhnlich macht, aber immer noch nicht ideal.
Charles Duffy
5
Auf dem Mac müssen Sie PROMPT_COMMAND="__prompt_command; ${PROMPT_COMMAND}"das Öffnen eines neuen Tabs / Fensters im aktuellen Arbeitsverzeichnis aktivieren, das weiterhin funktioniert. PROMPT_COMMAND=update_terminal_cwdStandardmäßig wird der CWD protokolliert.
Isaac Turner
3
Die Bash-Variable PIPESTATUSbietet weitere Informationen zum zuletzt ausgeführten Befehl oder zur zuletzt ausgeführten Pipe. ${PIPESTATUS[@]}wird eine Folge von Nullen sein, wie 0 0 0für den Fall, dass alle Befehle korrekt ausgeführt wurden, andernfalls hat es keine Nullen wie 0 127 1. Man kann mit auf Erfolg prüfen if $(echo ${PIPESTATUS[@]} | grep -qEe '^0( 0)*$'); then echo "good"; else echo "bad"; fi.
Nik O'Lai
18

Wenn Sie den Eingabeaufforderungsbefehl nicht verwenden möchten, müssen Sie zwei Dinge berücksichtigen:

  1. den Wert von $ bekommen? vor allem anderen, sonst wird es überschrieben
  2. Escape alle $ 's in der PS1 (so wird es nicht ausgewertet, wenn Sie es zuweisen)

Arbeitsbeispiel mit einer Variablen

PS1="\$(VALU="\$?" ; echo \$VALU ; date ; if [ \$VALU == 0 ]; then echo zero; else echo nonzero; fi) " 

Arbeitsbeispiel ohne Variable

Hier muss das if das erste sein, bevor ein Befehl das überschreibt $?.

PS1="\$(if [ \$? == 0 ]; then echo zero; else echo nonzero; fi) "

Beachten Sie, wie das \$()maskiert wird, damit es nicht sofort ausgeführt wird, sondern jedes Mal, wenn PS1 verwendet wird. Auch alle Verwendungen von\$?

Helios
quelle
3
Das ist so unglaublich toll! Ich bin seit 1995 ein alter Dirty Bashtard und es ist mir nie in den Sinn gekommen, die Befehlsersetzung in meiner PS1 zu verwenden. Vielen Dank.
Bruno Bronosky
3
Obwohl ich eine einfache ternäre Notation bevorzuge:export PS1="\$([ \$? == 0 ] && echo ✅ || echo ⚠️ ) \h:\W \u\n\$ "
Bruno Bronosky
Das ist cool und ich bearbeite gerade meine .bashrc-Datei, um sie zu integrieren. Tiny nit: "-eq" anstelle von "==" (und "=" anstelle von "==").
Keithpjolley
Diese Lösung ist besonders interessant, wenn Tools wie Python verwendet werden, virtualenvdie PS1bei Aktivierung Variablen Variablen voranstellen . Verwenden PROMPT_COMMANDwürde nicht funktionieren!
Samb
Wenn Sie verwenden \${PIPESTATUS[-1]}, können Sie den Exit-Status des letzten Befehls abrufen, auch wenn dies nicht das erste in Ihrem Befehl ist PS1.
David Ongaro
7

Ich wollte die Standardfarben von Debian beibehalten, den genauen Code drucken und ihn nur bei einem Fehler drucken:

# Show exit status on failure.
PROMPT_COMMAND=__prompt_command

__prompt_command() {
    local curr_exit="$?"

    local BRed='\[\e[0;91m\]'
    local RCol='\[\e[0m\]'

    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

    if [ "$curr_exit" != 0 ]; then
        PS1="[${BRed}$curr_exit${RCol}]$PS1"
    fi
}
Velkan
quelle
1

Kompakte Lösung:

PS1='... $(code=${?##0};echo ${code:+[error: ${code}]})'

Dieser Ansatz erfordert nicht PROMPT_COMMAND(anscheinend kann dies manchmal langsamer sein) und wird gedruckt, [error: <code>]wenn der Exit-Code nicht Null ist, und nichts, wenn er Null ist:

... > false
... [error: 1]> true
... >

Ändern Sie das [error: ${code}]Teil nach Ihren Wünschen, ${code}wobei der zu druckende Code ungleich Null ist.

Beachten Sie die Verwendung von, 'um sicherzustellen, dass die Inline- $()Shell ausgeführt wird, wenn PS1 später ausgewertet wird, und nicht, wenn die Shell gestartet wird.

Als Bonus können Sie es in Rot bunt machen, indem Sie es \e[01;31mvor und \e[00mnach dem Zurücksetzen hinzufügen :

PS1='... \e[01;31m$(code=${?##0};echo ${code:+[error: ${code}]})\e[00m'

- -

Wie es funktioniert:

  • Es verwendet die Bash-Parameter-Ersetzung
  • Zuerst ${?##0}liest der den Exit-Code $?des vorherigen Befehls
  • Das ##wird jedes 0Muster von Anfang an entfernen und ein 0Ergebnis effektiv zu einer leeren Variable machen (danke @blaskovicz für den Trick).
  • Wir weisen dies einer temporären codeVariablen zu, da wir eine weitere Ersetzung vornehmen müssen, und sie können nicht verschachtelt werden
  • Das ${code:+REPLACEMENT}druckt das REPLACEMENTTeil nur, wenn die Variable codegesetzt ist (nicht leer).
  • Auf diese Weise können wir Text und Klammern hinzufügen und die Variable erneut inline referenzieren: [error: ${code}]
Alexander Klimetschek
quelle
0

@Demure verbessert

Ich denke, das ist wichtig, weil es nicht immer den Exit-Status 0 oder 1 gibt.

if [ $EXIT != 0 ]; then
    PS1+="${Red}${EXIT}:\u${RCol}"      # Add red if exit code != 0
else
    PS1+="${Gre}${EXIT}:\u${RCol}"      # Also displays exit status
fi
pevik
quelle
2
Gibt es einen Grund, warum Sie die Erweiterung nicht zitieren? Wenn ein Benutzer mit diesem PROMPT_COMMAND festgelegt ist IFS=0, wird dies if [ != 0 ]bei der Erweiterung (natürlich mit einem Null-Exit-Code). Wenn Sie if [ "$EXIT" != 0 ]es machen, wird das vermieden.
Charles Duffy
0

Um das ursprüngliche Eingabeaufforderungsformat (nicht nur Farben) beizubehalten, können Sie Folgendes an das Ende von anhängen ~/.bashrc:

PS1_ORIG=$PS1 # original primary prompt value
PROMPT_COMMAND=__update_prompt # Func to be re-evaluated after each command is executed
__update_prompt() {
    local PREVIOUS_EXIT_CODE="$?"
    if [ $PREVIOUS_EXIT_CODE != 0 ]; then
        local RedCol='\[\e[0;31m\]'
        local ResetCol='\[\e[0m\]'
        local replacement="${RedCol}\u${ResetCol}"

        # Replace username color
        PS1=${PS1_ORIG//]\\u/]$replacement}
        ## Alternative: keep same colors, append exit code
        #PS1="$PS1_ORIG[${RedCol}error=$PREVIOUS_EXIT_CODE${ResetCol}]$ "
    else
        PS1=$PS1_ORIG
    fi
}

Siehe auch den Kommentar zum alternativen Ansatz, bei dem die Farbe des Benutzernamens beibehalten und nur der Fehlercode in Rot an das Ende des ursprünglichen Eingabeaufforderungsformats angehängt wird

atsu85
quelle
Diese Lösung basiert in gewisser Weise auf Ideen / Lösungen von Demure und Velkan (behält jedoch das ursprüngliche Format und die Farben bei)
atsu85
0

Warum habe ich selbst nicht darüber nachgedacht?) Ich fand das sehr interessant und fügte diese Funktion meinem ' Info-Bar' -Projekt hinzu. Die Augen werden rot, wenn der letzte Befehl fehlgeschlagen ist.

#!/bin/bash
eyes=(O o ∘ ◦ ⍤ ⍥) en=${#eyes[@]} mouth='_'                                                           
face () { # gen random face                                                                           
    [[ $error -gt 0 ]] && ecolor=$RED || ecolor=$YLW                                                  
    if [[ $1 ]]; then printf "${eyes[$[RANDOM%en]]}$mouth${eyes[$[RANDOM%en]]}"                       
                 else printf "$ecolor${eyes[$[RANDOM%en]]}$YLW$mouth$ecolor${eyes[$[RANDOM%en]]}$DEF"  
    fi                                                                                                
}                                                                                                     
info () { error=$?                                                                                    
    [[ -d .git ]] && {  # If in git project folder add git status to info bar output                  
        git_clr=('GIT' $(git -c color.ui=always status -sb)) # Colored output 4 info                  
        git_tst=('GIT' $(git                    status -sb)) # Simple  output 4 test                  
    }                                                                                                 
    printf -v line "%${COLUMNS}s"                            # Set border length                      
    date=$(printf "%(%a %d %b %T)T")                         # Date & time 4 test                     
    test=" O_o $PWD  ${git_tst[*]} $date o_O "               # Test string                            
    step=$[$COLUMNS-${#test}]; [[ $step -lt 0 ]] && step=0   # Count spaces                           
    line="$GRN${line// /-}$DEF\n"                            # Create lines                           
    home="$BLD$BLU$PWD$DEF"                                  # Home dir info                          
    date="$DIM$date$DEF"                                     # Colored date & time                    
           #------+-----+-------+--------+-------------+-----+-------+--------+                       
           # Line | O_o |homedir| Spaces | Git  status | Date|  o_O  |  Line  |                       
           #------+-----+-------+--------+-------------+-----+-------+--------+                       
    printf "$line $(face) $home %${step}s ${git_clr[*]} $date $(face) \n$line" # Final info string    
}                                                                                                     
PS1='${debian_chroot:+($debian_chroot)}\n$(info)\n$ '                                                 
case "$TERM" in xterm*|rxvt*)                                                                         
    PS1="\[\e]0;${debian_chroot:+($debian_chroot)} $(face 1) \w\a\]$PS1";;                            
esac 

Geben Sie hier die Bildbeschreibung ein

Ivan
quelle