Was ist der Unterschied zwischen PS1 und PROMPT_COMMAND?

108

Beim Betrachten dieses fantastischen Threads habe ich festgestellt, dass einige Beispiele verwendet werden

PS1="Blah Blah Blah"

und einige verwenden

PROMPT_COMMAND="Blah Blah Blah"

(und einige verwenden beide), wenn Sie die Eingabeaufforderung in einer Bash-Shell festlegen. Was ist der Unterschied zwischen den beiden? Eine SO-Suche und sogar ein bisschen breitere Google-Suche bringen mir keine Ergebnisse, daher wäre sogar ein Link zum richtigen Ort, um nach der Antwort zu suchen, willkommen.

Jed Daniels
quelle

Antworten:

59

Auf der GNU Bash-Dokumentseite: http://www.gnu.org/software/bash/manual/bashref.html

PROMPT_COMMAND
    If set, the value is interpreted as a command to execute before
    the printing of each primary prompt ($PS1).

Ich habe es nie benutzt, aber ich hätte es zurück benutzen können, wenn ich nur sh gehabt hätte.

Scott Thomson
quelle
67

PROMPT_COMMAND kann gewöhnliche Bash-Anweisungen enthalten, während die PS1-Variable auch Sonderzeichen wie '\ h' für den Hostnamen in der Variablen enthalten kann.

Zum Beispiel ist hier meine Bash-Eingabeaufforderung, die sowohl PROMPT_COMMAND als auch PS1 verwendet. Der Bash-Code in PROMPT_COMMAND ermittelt, in welchem ​​Git-Zweig Sie sich möglicherweise befinden, und zeigt diesen an der Eingabeaufforderung zusammen mit dem Exit-Status des zuletzt ausgeführten Prozesses, dem Hostnamen und dem Basisnamen des pwd an. Die Variable RET speichert den Rückgabewert des zuletzt ausgeführten Programms. Dies ist praktisch, um festzustellen, ob ein Fehler aufgetreten ist, und um den Fehlercode des letzten Programms anzuzeigen, das ich im Terminal ausgeführt habe. Beachten Sie das äußere ', das den gesamten PROMPT_COMMAND-Ausdruck umgibt. Es enthält PS1, sodass diese Variable jedes Mal neu ausgewertet wird, wenn die Variable PROMPT_COMMAND ausgewertet wird.

PROMPT_COMMAND='RET=$?;\
  BRANCH="";\
  ERRMSG="";\
  if [[ $RET != 0 ]]; then\
    ERRMSG=" $RET";\
  fi;\
  if git branch &>/dev/null; then\
    BRANCH=$(git branch 2>/dev/null | grep \* |  cut -d " " -f 2);\
  fi;
PS1="$GREEN\u@\h $BLUE\W $CYAN$BRANCH$RED$ERRMSG \$ $LIGHT_GRAY";'

Die Beispielausgabe sieht in einem Nicht-Git-Verzeichnis folgendermaßen aus:

sashan@dhcp-au-122 Documents  $ false
sashan@dhcp-au-122 Documents  1 $ 

und in einem Git-Verzeichnis sehen Sie den Filialnamen:

sashan@dhcp-au-122 rework mybranch $ 

Aktualisieren

Nachdem ich die Kommentare und Bobs Antwort gelesen habe, denke ich, dass es besser ist, sie so zu schreiben, wie er es beschreibt. Es ist wartbarer als das, was ich ursprünglich oben geschrieben habe, wo die PS1-Variable im PROMPT_COMMAND festgelegt ist, einer super komplizierten Zeichenfolge, die zur Laufzeit von bash ausgewertet wird. Es funktioniert, aber es ist komplizierter als es sein muss. Um fair zu sein, habe ich PROMPT_COMMAND vor ungefähr 10 Jahren für mich selbst geschrieben und es hat funktioniert und ich habe nicht zu viel darüber nachgedacht.

Für diejenigen, die neugierig sind, wie ich meine Sachen geändert habe, habe ich den Code für PROMPT_COMMAND im Grunde genommen in eine separate Datei gestellt (wie von Bob beschrieben) und dann die Zeichenfolge wiedergegeben, die ich als PS1 beabsichtige:

GREEN="\[\033[0;32m\]"
CYAN="\[\033[0;36m\]"
RED="\[\033[0;31m\]"
PURPLE="\[\033[0;35m\]"
BROWN="\[\033[0;33m\]"
LIGHT_GRAY="\[\033[0;37m\]"
LIGHT_BLUE="\[\033[1;34m\]"
LIGHT_GREEN="\[\033[1;32m\]"
LIGHT_CYAN="\[\033[1;36m\]"
LIGHT_RED="\[\033[1;31m\]"
LIGHT_PURPLE="\[\033[1;35m\]"
YELLOW="\[\033[1;33m\]"
WHITE="\[\033[1;37m\]"
RESTORE="\[\033[0m\]" #0m restores to the terminal's default colour

if [ -z $SCHROOT_CHROOT_NAME ]; then
    SCHROOT_CHROOT_NAME=" "
fi
BRANCH=""
ERRMSG=""
RET=$1
if [[ $RET != 0 ]]; then
    ERRMSG=" $RET"
fi
if which git &>/dev/null; then
    BRANCH=$(git branch 2>/dev/null | grep \* |  cut -d " " -f 2)
else
    BRANCH="(git not installed)"
fi
echo "${GREEN}\u@\h${SCHROOT_CHROOT_NAME}${BLUE}\w \
${CYAN}${BRANCH}${RED}${ERRMSG} \$ $RESTORE"

und in meinem .bashrc

function prompt_command {
    RET=$?
    export PS1=$(~/.bash_prompt_command $RET)
}
PROMPT_DIRTRIM=3
export PROMPT_COMMAND=prompt_command
Sashang
quelle
1
Sie könnten eine Ihrer Zeilen kürzen : if git branch &>/dev/null ; then\ . Es leitet sowohl stdout als auch stderr nach / dev / null um. tldp.org/LDP/abs/html/io-redirection.html
3
Es besteht keine Notwendigkeit zum Exportieren PROMPT_COMMAND .
Dolmen
2
Ich denke, Cevings Kommentar trifft auch für diese Antwort sehr zu:Don't set PS1 in PROMPT_COMMAND! Set variables in PROMPT_COMMAND and use them in PS1
phil294
2
Ich sehe keinen Grund, warum es nachteilig ist , PS1online zu wechseln PROMPT_COMMAND. Es ist perfekter nützlicher Code. Im Gegensatz zu Bobs Antwort wurde die PS1Variable korrekt konstruiert. Dies ermöglicht eine viel komplexere Bash-Eingabeaufforderung, abhängig von Ihrer tatsächlichen Situation.
Christian Wolf
2
@ChristianWolf Konstruktion von PS1innen hat PROMPT_COMMANDkeinen Zweck. Es ist ein Beispiel, wie man es nicht macht. Wenn Sie PS1einmal konstruiert haben .bash_profile, verwenden Sie einfach einfache Anführungszeichen anstelle von doppelten Anführungszeichen, damit die Ersetzungen von Variablen bei jeder Eingabeaufforderung ausgewertet werden.
Kumpel
46

Der Unterschied besteht darin, dass PS1 die tatsächlich verwendete Eingabeaufforderungszeichenfolge ist und PROMPT_COMMAND ein Befehl ist, der unmittelbar vor der Eingabeaufforderung ausgeführt wird. Wenn Sie die einfachste und flexibelste Methode zum Erstellen einer Eingabeaufforderung suchen, versuchen Sie Folgendes:

Fügen Sie dies in Ihre .bashrc ein:

function prompt_command {
  export PS1=$(~/bin/bash_prompt)
}
export PROMPT_COMMAND=prompt_command

Schreiben Sie dann ein Skript (Bash, Perl, Ruby: Ihre Wahl) und platzieren Sie es in ~ / bin / bash_prompt.

Das Skript kann beliebige Informationen verwenden, um eine Eingabeaufforderung zu erstellen. Dies ist IMO viel einfacher, da Sie nicht die etwas barocke Substitutionssprache lernen müssen, die nur für die PS1-Variable entwickelt wurde.

Sie könnten denken, dass Sie dasselbe tun könnten, indem Sie PROMPT_COMMAND einfach direkt auf ~ / bin / bash_prompt setzen und PS1 auf die leere Zeichenfolge setzen. Dies scheint zunächst zu funktionieren, aber Sie werden bald feststellen, dass der Readline-Code erwartet, dass PS1 auf die tatsächliche Eingabeaufforderung gesetzt wird, und wenn Sie im Verlauf durch die Backwords scrollen, werden die Dinge dadurch durcheinander gebracht. Diese Problemumgehung bewirkt, dass PS1 immer die neueste Eingabeaufforderung widerspiegelt (da die Funktion die tatsächliche PS1 festlegt, die von der aufrufenden Instanz der Shell verwendet wird). Dadurch funktionieren Readline- und Befehlsverlauf einwandfrei.

Bob
quelle
17
Nicht gesetzt PS1in PROMPT_COMMAND! Setzen Sie Variablen in PROMPT_COMMANDund verwenden Sie sie in PS1. Andernfalls verlieren Sie die Möglichkeit, die PS1Escape-Sequenzen wie \uoder zu verwenden \h. Sie müssen sie neu erfinden PROMPT_COMMAND. Das mag möglich sein, aber es ist nicht möglich, den Verlust von \[und \]das Markieren des Anfangs und des Endes von nicht druckbaren Zeichen zu umgehen. Dies bedeutet, dass Sie keine Farben verwenden können, ohne das Terminal über die Länge der Eingabeaufforderung zu verwirren. Und das verwirrt readlinebeim Bearbeiten eines Befehls, der zwei Zeilen erzeugt. Am Ende haben Sie ein großes Durcheinander auf dem Bildschirm.
Ceving
1
@ceving Stimmt das! Man kann PROMPT_COMMAND verwenden, um das Format deiner PS1 zu ändern und das Beste aus beiden Welten zu bekommen
2grit
3
PROMPT_COMMANDwird vor dem Drucken ausgeführt PS1. Ich sehe keine Probleme beim Einstellen PS1von innen PROMPT_COMMAND, da nach PROMPT_COMMANDAbschluss der Shell die Shell gedruckt wird PS1, die von PROMPT_COMMAND(oder in diesem Fall von innen prompt_command) geändert wurde .
Felipe Alvarez
3
Warnung: PROMPT_COMMAND sollte im Allgemeinen nicht zum direkten Drucken von Zeichen an die Eingabeaufforderung verwendet werden. Zeichen, die außerhalb von PS1 gedruckt werden, werden von Bash nicht gezählt, wodurch der Cursor falsch platziert und Zeichen gelöscht werden. Verwenden Sie entweder PROMPT_COMMAND, um PS1 festzulegen, oder sehen Sie sich die Einbettungsbefehle an. ( Arch Wiki Source )
Meffect
3
Ich verstehe nicht, warum jeder versucht, ein paar Tricks in PROMPT_COMMAND zu machen, anstatt nur die Befehlsersetzung in PS1 zu export PS1='$(~/bin/bash_prompt)'verwenden. Das gleiche Problem sieht vernünftig aus
Kumpel
10

Von man bash:

PROMPT_COMMAND

Wenn festgelegt, wird der Wert als Befehl ausgeführt, bevor jede primäre Eingabeaufforderung ausgegeben wird.

PS1

Der Wert dieses Parameters wird erweitert (siehe PROMPTING unten) und als primäre Eingabeaufforderungszeichenfolge verwendet. Der Standardwert ist '' \ s- \ v \ $ ''.

Wenn Sie einfach die Eingabeaufforderungszeichenfolge festlegen möchten, reicht die Verwendung PS1allein aus:

PS1='user \u on host \h$ '

Wenn Sie kurz vor dem Drucken der Eingabeaufforderung etwas anderes tun möchten, verwenden Sie PROMPT_COMMAND. Wenn Sie beispielsweise zwischengespeicherte Schreibvorgänge auf die Festplatte synchronisieren möchten, können Sie Folgendes schreiben:

PROMPT_COMMAND='sync'
Cyker
quelle
1
Sie können auch den Titel des Terminals aus gesetzt , PS1ohne dass PROMPT_COMMAND, wie die Sequenz, die den Titel festlegen kann in aufgenommen wird PS1mit eingewickelt \[und \].
Dolmen
1
@ Dolmen Alles klar. Lassen Sie uns dann etwas anderes tun, z. B. das dynamische Festlegen einer Umgebungsvariablen.
Cyker
@Cyker Sie können Umgebungsvariablen dynamisch festlegen PS1. Sie werden nur in der Unterschale festgelegt, sodass Sie ihren Wert nicht zurückerhalten können. aber Ihr Beispiel ist trivialPS1='$(sync)user \u on host \h$ '
Kumpel
1

Der Unterschied ist das

  • Wenn Sie eine unvollständige Zeile von ausgeben PROMPT_COMMAND, wird Ihre Bash-Eingabeaufforderung beschädigt
  • PS1Ersatz \Hund Freunde
  • PROMPT_COMMANDführt seinen Inhalt aus, PS1verwendet seinen Inhalt als Eingabeaufforderung.

PS1Führt an jeder Eingabeaufforderung eine Variablenerweiterung und Befehlssubstitution durch, ohne dass ein PROMPT_COMMANDWert zugewiesen PS1oder ein beliebiger Code ausgeführt werden muss. Sie können dies einfach export PS1='$(uuidgen) $RANDOM'einmal tun, indem Sie einfach .bash_profileeinfache Anführungszeichen verwenden

Kumpel
quelle
0

Ja, um zu versuchen, das wirklich festzunageln:

  • PROMPT_COMMANDist eine praktische Bash- Convenience-Variable / -Funktion, aber es gibt streng genommen nichts, was nicht auch PS1alleine gemacht werden kann , richtig?

Ich meine, wenn man eine andere Variable mit einem Gültigkeitsbereich außerhalb der Eingabeaufforderung festlegen möchte : Abhängig von der Shell müsste diese Variable wahrscheinlich zuerst außerhalb deklariert werden, oder (im schlimmsten Fall) muss man sich mit etwas auseinandersetzen, das vorher auf ein FIFO wartet anrufen (und am Ende wieder bewaffnet ); das könnte einige Probleme verursachen, besonders wenn Sie einige ausgefallene regex verwenden; aber sonst: kann man alles erreichen, indem man die Befehlssubstitution innerhalb (und vielleicht in Eckfällen explizite Subshells) verwendet?$PS1$PS1$PS1\u \hPROMPT_COMMAND$PS1

Richtig?

Geoff Nixon
quelle