Jaka jest różnica między PS1 a PROMPT_COMMAND

108

Przeglądając ten niesamowity wątek zauważyłem, że w niektórych przykładach jest

PS1="Blah Blah Blah"

i trochę pożytku

PROMPT_COMMAND="Blah Blah Blah"

(a niektórzy używają obu) podczas ustawiania zachęty w powłoce bash. Jaka jest różnica między nimi? Wyszukiwanie SO, a nawet trochę szersze wyszukiwanie w Google nie dają mi wyników, więc nawet link do odpowiedniego miejsca, w którym można szukać odpowiedzi, byłby mile widziany.

Jed Daniels
źródło

Odpowiedzi:

59

Ze strony dokumentacji GNU Bash: 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).

Nigdy go nie używałem, ale mogłem go użyć, kiedy miałem tylko sh.

Scott Thomson
źródło
67

PROMPT_COMMAND może zawierać zwykłe instrukcje bash, podczas gdy zmienna PS1 może również zawierać w zmiennej znaki specjalne, takie jak „\ h” dla nazwy hosta.

Na przykład tutaj jest moja zachęta bash, która używa zarówno PROMPT_COMMAND, jak i PS1. Kod bash w PROMPT_COMMAND sprawdza, w jakiej gałęzi git możesz się znajdować i wyświetla to po znaku zachęty, wraz z kodem zakończenia ostatniego uruchomionego procesu, nazwą hosta i podstawową nazwą pwd. Zmienna RET przechowuje wartość zwracaną przez ostatnio wykonany program. Jest to wygodne, aby sprawdzić, czy wystąpił błąd i kod błędu ostatniego programu, który uruchomiłem w terminalu. Zwróć uwagę na zewnętrzne „otaczające całe wyrażenie PROMPT_COMMAND. Zawiera PS1, więc ta zmienna jest ponownie oceniana za każdym razem, gdy oceniana jest zmienna 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";'

Przykładowe wyjście wygląda następująco w katalogu innym niż git:

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

aw katalogu git zobaczysz nazwę gałęzi:

sashan@dhcp-au-122 rework mybranch $ 

Aktualizacja

Po przeczytaniu komentarzy i odpowiedzi Boba myślę, że pisanie tego tak, jak opisuje, jest lepsze. Jest łatwiejszy w utrzymaniu niż to, co pierwotnie napisałem powyżej, gdzie zmienna PS1 jest ustawiana wewnątrz PROMPT_COMMAND, który sam jest bardzo skomplikowanym ciągiem, który jest oceniany w czasie wykonywania przez bash. Działa, ale jest bardziej skomplikowane, niż powinno. Aby być uczciwym napisałem to PROMPT_COMMAND dla siebie około 10 lat temu i zadziałało i nie myślałem o tym zbyt wiele.

Dla tych, którzy są ciekawi, jak zmieniłem swoje rzeczy, po prostu umieściłem kod PROMPT_COMMAND w oddzielnym pliku (jak opisał Bob), a następnie powtórzyłem ciąg, który mam zamiar być 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"

i w moim .bashrc

function prompt_command {
    RET=$?
    export PS1=$(~/.bash_prompt_command $RET)
}
PROMPT_DIRTRIM=3
export PROMPT_COMMAND=prompt_command
sashang
źródło
1
Można skrócić jeden ze swoich wierszy: if git branch &>/dev/null ; then\ . Przekierowuje zarówno stdout, jak i stderr do / dev / null. tldp.org/LDP/abs/html/io-redirection.html
3
Nie ma potrzeby eksportu PROMPT_COMMAND .
dolmen,
2
Myślę, że komentarz Cevinga jest bardzo prawdziwy również dla tej odpowiedzi:Don't set PS1 in PROMPT_COMMAND! Set variables in PROMPT_COMMAND and use them in PS1
phil294
2
Nie widzę powodu, dla którego zmiana PS1online w ciągu PROMPT_COMMANDjest niekorzystna. To doskonały, przydatny kod. W przeciwieństwie do odpowiedzi Boba, PS1zmienna została poprawnie skonstruowana. Pozwala to na znacznie bardziej wyrafinowany monit basha w zależności od aktualnej sytuacji.
Christian Wolf,
2
Konstrukcja PS1wnętrza @ChristianWolf PROMPT_COMMANDjest bezcelowa. jest przykładem, jak tego nie robić. konstruuj PS1raz w .bash_profile, po prostu użyj pojedynczych cudzysłowów zamiast podwójnych cudzysłowów, aby podstawienia zmiennych były oceniane podczas każdego zachęty.
kumpel
46

Różnica polega na tym, że PS1 jest rzeczywiście używanym ciągiem zachęty, a PROMPT_COMMAND jest poleceniem wykonywanym tuż przed zachętą. Jeśli chcesz mieć najprostszy i najbardziej elastyczny sposób tworzenia monitu, wypróbuj następujące rozwiązania:

Umieść to w swoim .bashrc:

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

Następnie napisz skrypt (bash, perl, ruby: twój wybór) i umieść go w ~ / bin / bash_prompt.

Skrypt może użyć dowolnych informacji do skonstruowania zachęty. Jest to znacznie prostsze IMO, ponieważ nie musisz uczyć się nieco barokowego języka zastępowania, który został opracowany tylko dla zmiennej PS1.

Możesz pomyśleć, że możesz zrobić to samo, po prostu ustawiając PROMPT_COMMAND bezpośrednio na ~ / bin / bash_prompt i ustawiając PS1 na pusty łańcuch. Na początku wydaje się to działać, ale wkrótce odkrywasz, że kod readline oczekuje, że PS1 zostanie ustawiony na właściwy znak zachęty, a kiedy przewijasz wsteczne hasła w historii, w rezultacie wszystko się pogmatwa. To obejście powoduje, że PS1 zawsze odzwierciedla najnowszy znak zachęty (ponieważ funkcja ustawia rzeczywisty PS1 używany przez wywołujące wystąpienie powłoki), a to sprawia, że ​​readline i historia poleceń działają dobrze.

Pion
źródło
17
Nie należy ustawiać PS1w PROMPT_COMMAND! Ustaw zmienne PROMPT_COMMANDi używaj ich w programie PS1. W przeciwnym razie stracisz możliwość korzystania z PS1sekwencji ucieczki, takich jak \ulub \h. Musisz je wymyślić na nowo PROMPT_COMMAND. Może to być możliwe, ale nie da się obejść utraty \[i \]oznaczania początku i końca niedrukowalnych znaków. Oznacza to, że nie można używać kolorów bez pomylenia terminala co do długości podpowiedzi. I to jest mylące readlinepodczas edycji polecenia powodującego powstanie dwóch linii. W końcu na ekranie jest wielki bałagan.
ceving
1
@ceving To prawda! Można użyć PROMPT_COMMAND, aby zmienić format PS1 i uzyskać to, co najlepsze z obu światów
2grit
3
PROMPT_COMMANDjest wykonywana przed drukowaniem PS1. Nie widzę problemów PS1z ustawieniem od wewnątrz PROMPT_COMMAND, bo po PROMPT_COMMANDskończeniu będzie drukować powłoka PS1, która była modyfikowana od PROMPT_COMMAND(czy w tym przypadku od wewnątrz prompt_command)?
Felipe Alvarez
3
Ostrzeżenie: PROMPT_COMMAND generalnie nie powinno być używane do drukowania znaków bezpośrednio w zachęcie. Znaki drukowane poza PS1 nie są liczone przez Bash, co spowoduje nieprawidłowe umieszczenie kursora i wyczyszczenie znaków. Użyj PROMPT_COMMAND, aby ustawić PS1 lub spójrz na polecenia osadzania. ( Arch Wiki Source )
meffect
3
Nie rozumiem, dlaczego wszyscy próbują robić jakieś sztuczki w PROMPT_COMMAND zamiast po prostu zastępować polecenia w PS1, export PS1='$(~/bin/bash_prompt)'robi to samo, błąd wygląda rozsądnie
kolego
10

Od man bash:

PROMPT_COMMAND

Jeśli jest ustawiona, wartość jest wykonywana jako polecenie przed wydaniem każdego podstawowego monitu.

PS1

Wartość tego parametru jest interpretowana (patrz ZACHĘTY poniżej) i używana jako podstawowy łańcuch zachęty. Wartość domyślna to `` \ s- \ v \ $ ''.

Jeśli chcesz po prostu ustawić ciąg zachęty, PS1wystarczy użyć samego:

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

Jeśli chcesz zrobić coś innego tuż przed wydrukowaniem monitu, użyj PROMPT_COMMAND. Na przykład, jeśli chcesz zsynchronizować zapisane w pamięci podręcznej zapisy na dysku, możesz napisać:

PROMPT_COMMAND='sync'
Cyker
źródło
1
Możesz także ustawić tytuł terminala z PS1bez potrzeby PROMPT_COMMAND, ponieważ sekwencja, która ustawia tytuł, może być zawarta w PS1opakowaniach \[i \].
dolmen
1
@dolmen W porządku. Następnie zróbmy coś innego, na przykład dynamiczne ustawienie zmiennej środowiskowej.
Cyker,
@Cyker możesz dynamicznie ustawić zmienną środowiskową w PS1, po prostu zostanie ona ustawiona w podpowłoce, więc nie możesz odzyskać jej wartości. ale twój przykład jest trywialnyPS1='$(sync)user \u on host \h$ '
kolego,
1

różnica polega na tym

  • jeśli wypiszesz niekompletną linię z PROMPT_COMMAND, spieprzy to twój znak zachęty bash
  • PS1 substytuty \H i przyjaciele
  • PROMPT_COMMANDuruchamia swoją zawartość, PS1używa jej zawartości jako zachęty.

PS1wykonuje interpretację zmiennych i podstawianie poleceń w każdym znaku zachęty, nie ma potrzeby PROMPT_COMMANDprzypisywania wartości do PS1lub uruchamiania dowolnego kodu. możesz łatwo zrobić export PS1='$(uuidgen) $RANDOM'raz w .bash_profile, po prostu użyj pojedynczych cudzysłowów

kumpel
źródło
0

Tak, więc spróbuj naprawdę to naprawić:

  • PROMPT_COMMANDto poręczna zmienna / funkcja zapewniająca wygodę basha , ale ściśle mówiąc, nie ma niczego, czego nie można zrobić PS1samodzielnie, prawda?

Chodzi mi o to, że jeśli ktoś chce ustawić inną zmienną z zakresem poza monitem: w zależności od powłoki, ta zmienna prawdopodobnie musiałaby zostać zadeklarowana jako pierwsza na zewnątrz $PS1lub (w najgorszym przypadku) można by wymyślić coś czekającego na FIFO przed dzwoni $PS1(i ponownie uzbrojony pod koniec $PS1); \u \hmoże powodować pewne problemy, zwłaszcza jeśli używasz wymyślnej regex; ale poza tym: można osiągnąć wszystko, co PROMPT_COMMANDmożna, stosując podstawianie poleceń w obrębie $PS1(i, może w przypadkach narożnych, jawnych podpowłok)?

Dobrze?

Geoff Nixon
źródło