Qual è la differenza tra PS1 e PROMPT_COMMAND


108

Dando uno sguardo a questo fantastico thread ho notato che alcuni esempi usano

PS1="Blah Blah Blah"

e un po 'di utilità

PROMPT_COMMAND="Blah Blah Blah"

(e alcuni usano entrambi) quando si imposta il prompt in una shell bash. Qual è la differenza tra i due? Una ricerca SO e anche un po 'più ampia di ricerca su Google non mi stanno dando risultati, quindi sarebbe apprezzato anche un link al posto giusto per cercare la risposta.

Risposte:



67

PROMPT_COMMAND può contenere istruzioni bash ordinarie mentre la variabile PS1 può contenere anche i caratteri speciali, come "\ h" per hostname, nella variabile.

Ad esempio, ecco il mio prompt di bash che utilizza sia PROMPT_COMMAND che PS1. Il codice bash in PROMPT_COMMAND calcola in quale ramo git potresti trovarti e lo visualizza al prompt, insieme allo stato di uscita dell'ultimo processo eseguito, hostname e basename del pwd. La variabile RET memorizza il valore di ritorno dell'ultimo programma eseguito. Questo è comodo per vedere se c'è stato un errore e il codice di errore dell'ultimo programma che ho eseguito nel terminale. Notare l'esterno 'che circonda l'intera espressione PROMPT_COMMAND. Include PS1 in modo che questa variabile venga rivalutata ogni volta che viene valutata la variabile PROMPT_COMMAND.

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";'

L'output di esempio è simile a questo in una directory non git:

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

e in una directory git vedi il nome del ramo:

sashan@dhcp-au-122 rework mybranch $ 

Aggiornare

Dopo aver letto i commenti e la risposta di Bob, penso che scriverlo come lui descrive sia meglio. È più gestibile di quello che ho scritto originariamente sopra, dove la variabile PS1 è impostata all'interno di PROMPT_COMMAND, che a sua volta è una stringa super complicata che viene valutata in fase di esecuzione da bash. Funziona, ma è più complicato di quanto dovrebbe essere. Per essere onesti, ho scritto PROMPT_COMMAND per me stesso circa 10 anni fa e ha funzionato e non ci ho pensato troppo.

Per chi è curioso di sapere come ho modificato le mie cose, ho praticamente messo il codice per PROMPT_COMMAND in un file separato (come descritto da Bob) e poi echo la stringa che intendo essere PS1:

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"

e nel mio .bashrc

function prompt_command {
    RET=$?
    export PS1=$(~/.bash_prompt_command $RET)
}
PROMPT_DIRTRIM=3
export PROMPT_COMMAND=prompt_command

1
Si potrebbe ridurre una delle tue linee: if git branch &>/dev/null ; then\ . Reindirizza sia stdout che stderr a / dev / null. tldp.org/LDP/abs/html/io-redirection.html

3
Non è necessario esportare PROMPT_COMMAND .
dolmen

2
Penso che il commento di ceving sia molto vero anche per questa risposta:Don't set PS1 in PROMPT_COMMAND! Set variables in PROMPT_COMMAND and use them in PS1
phil294

2
Non vedo un motivo per cui cambiare PS1online all'interno PROMPT_COMMANDè svantaggioso. È un codice utile perfetto. In contrasto con la risposta di Bob in basso, la PS1variabile è stata costruita correttamente. Ciò consente un prompt di bash molto più sofisticato a seconda della situazione attuale.
Christian Wolf

2
@ChristianWolf costruzione di PS1dentro PROMPT_COMMANDnon serve a nulla. è un esempio di come non farlo. costruire PS1una volta dentro .bash_profile, basta usare virgolette singole invece di virgolette doppie, in modo che le sostituzioni di variabili verranno valutate durante ogni prompt.
amico

46

La differenza è che PS1 è l'effettiva stringa di prompt utilizzata e PROMPT_COMMAND è un comando che viene eseguito appena prima del prompt. Se desideri il modo più semplice e flessibile per creare un prompt, prova questo:

Metti questo nel tuo .bashrc:

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

Quindi scrivi uno script (bash, perl, ruby: a tua scelta) e posizionalo in ~ / bin / bash_prompt.

Lo script può utilizzare tutte le informazioni che desidera per costruire un prompt. Questo è molto più semplice IMO perché non devi imparare il linguaggio di sostituzione un po 'barocco che è stato sviluppato solo per la variabile PS1.

Potresti pensare di poter fare lo stesso semplicemente impostando PROMPT_COMMAND direttamente su ~ / bin / bash_prompt e impostando PS1 sulla stringa vuota. All'inizio sembra funzionare, ma presto scopri che il codice readline si aspetta che PS1 sia impostato sul prompt effettivo, e quando scorri le backword nella cronologia, le cose si incasinano di conseguenza. Questa soluzione alternativa fa sì che PS1 rifletta sempre il prompt più recente (poiché la funzione imposta l'effettiva PS1 utilizzata dall'istanza invocante della shell), e questo fa sì che readline e la cronologia dei comandi funzionino correttamente.


17
Non impostato PS1in PROMPT_COMMAND! Imposta le variabili PROMPT_COMMANDe usale in PS1. Altrimenti perderai la possibilità di usare le PS1sequenze di fuga come \uo \h. Devi reinventarli PROMPT_COMMAND. Ciò potrebbe essere possibile ma non è possibile aggirare la perdita di \[e \]che segnano l'inizio e la fine dei caratteri non stampabili. Ciò significa che non è possibile utilizzare i colori senza confondere il terminale sulla lunghezza del prompt. E questo crea confusione readlinequando si modifica un comando che genera due righe. Alla fine hai un gran casino sullo schermo.
ceving

1
@ceving True that! Si può utilizzare PROMPT_COMMAND per cambiare il formato della PS1 e ottenere il meglio da entrambi i mondi
2grit

3
PROMPT_COMMANDviene eseguito prima della stampa PS1. Non vedo problemi di impostazione PS1dall'interno PROMPT_COMMAND, perché al PROMPT_COMMANDtermine verrà stampata la shell PS1, quale è stata modificata da PROMPT_COMMAND(o in questo caso, dall'interno prompt_command)?
Felipe Alvarez

3
Avvertenza: PROMPT_COMMAND in genere non deve essere utilizzato per stampare i caratteri direttamente nel prompt. I caratteri stampati al di fuori di PS1 non vengono conteggiati da Bash, il che farà posizionare il cursore in modo errato e cancellare i caratteri. Usa PROMPT_COMMAND per impostare PS1 o guarda i comandi di incorporamento. ( Arch Wiki Source )
meffect

3
Non capisco perché tutti provino a fare alcuni trucchi in PROMPT_COMMAND invece di usare solo la sostituzione dei comandi in PS1 export PS1='$(~/bin/bash_prompt)'fa la stessa cosa, il bug sembra sano
amico

10

Da man bash:

PROMPT_COMMAND

Se impostato, il valore viene eseguito come comando prima di emettere ogni prompt principale.

PS1

Il valore di questo parametro viene espanso (vedere PROMPTING di seguito) e utilizzato come stringa di prompt principale. Il valore predefinito è "\ s- \ v \ $".

Se vuoi semplicemente impostare la stringa del prompt, PS1è sufficiente usare da solo:

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

Se vuoi fare qualcos'altro appena prima di stampare il prompt, usa PROMPT_COMMAND. Ad esempio, se desideri sincronizzare le scritture memorizzate nella cache su disco, puoi scrivere:

PROMPT_COMMAND='sync'

1
È inoltre possibile impostare il titolo del terminale PS1senza bisogno PROMPT_COMMAND, poiché la sequenza che ha impostato il titolo può essere inclusa PS1racchiusa tra \[e \].
dolmen

1
@dolmen Va bene. Quindi facciamo qualcos'altro, come impostare dinamicamente una variabile d'ambiente.
Cyker

@Cyker in cui puoi impostare dinamicamente la variabile di ambiente PS1, verrà semplicemente impostata in subshell, quindi non puoi recuperare il suo valore. ma il tuo esempio è banalePS1='$(sync)user \u on host \h$ '
amico

1

la differenza è questa

  • se produci una riga incompleta da PROMPT_COMMAND, rovinerà il tuo prompt di bash
  • PS1 sostituti \H e amici
  • PROMPT_COMMANDesegue il suo contenuto, PS1usa il suo contenuto come prompt.

PS1esegue l'espansione delle variabili e la sostituzione dei comandi a ogni prompt, non è necessario utilizzare PROMPT_COMMANDper assegnare un valore PS1o per eseguire codice arbitrario. puoi farlo facilmente export PS1='$(uuidgen) $RANDOM'una volta dentro .bash_profile, basta usare virgolette singole


0

Sì, quindi per provare a inchiodare davvero questo:

  • PROMPT_COMMANDè una pratica variabile / funzione di convenienza bash , ma non c'è, in senso stretto, nulla che non possa essere fatto anche da PS1solo, corretto?

Voglio dire, se si vuole impostare un'altra variabile con scope al di fuori del prompt: a seconda della shell, quella variabile dovrebbe probabilmente essere dichiarata prima all'esterno $PS1o (nel peggiore dei casi) si potrebbe dover avere fantasia con qualcosa in attesa su un FIFO prima di chiamando $PS1(e armato di nuovo alla fine del $PS1); la \u \hpotrebbe causare qualche problema, soprattutto se si sta utilizzando un po 'di fantasia regex; ma altrimenti: si può ottenere qualsiasi cosa PROMPT_COMMANDutilizzando la sostituzione dei comandi all'interno$PS1 (e, forse nei casi d'angolo, subshell esplicite)?

Destra?

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.