Skip to content

nifty shell prompt dings

Über seinen Shellprompt hat wohl jeder Unix-Geek schon geschrieben. In diesem Artikel mache ich das auch mal.

Meiner sieht im Default so aus:

[1/4998]mh@swivel:~ $ 

Langweilig, nicht? Das habe ich absichtlich so gemacht, denn von Zeit zu Zeit muss ich mir einen Account mit anderen Leuten teilen, und da hat man sowieso nicht die Möglichkeit, zu individualisieren. Deswegen weiche ich hier wie auch in anderen Bereichen so wenig wie möglich vom Default ab. Das ist auch der Grund, warum ich die bash als Shell verwende und keine luxuriöseren Dinge wie z.B. die zsh: Die hat man im Zweifel dort, wo man unter Streß arbeiten muss, nicht zur Verfügung und hat genau dort dann einen ganzen Sack von Features, an deren Vorhandensein man sich selbstverständlich gewöhnt hat, nicht zur Verfügung.

Der einzige Luxus, den ich mir neben Username, Hostname und dem aktuellen Directory im Standardprompt gönne, sind die History-Nummern, mit deren Hilfe ich ganz einfach Kommandos wiederholen kann:

[6/5003]mh@swivel:~ $ echo foo
foo
[7/5004]mh@swivel:~ $ echo bar
bar
[8/5005]mh@swivel:~ $ !5003
echo foo
foo
[9/5006]mh@swivel:~ $ 

Mit dem Ausrufungszeichen kann man Kommando 5003 neu ausgeführt. Das ist praktisch, wenn man ein komplexeres Kommando, das schon eine Weile zurückliegt, aber noch nicht rausgescrolled ist, schnell wiederholen möchte. Liegt es noch weiter zurück, ist man mit dem mit Ctrl-R ausgelösten reverse incremental search in der Shellhistory schneller.

Noch praktischer wird es nach einem "bind space:magic-space", dann bekommt man nämlich das Kommando editierbereit zurückgeholt:

[9/5042]mh@swivel:~ $ echo foo
foo
[10/5043]mh@swivel:~ $ echo bar
bar
[11/5044]mh@swivel:~ $ !5042

Drücke ich nun nach der 5042 einen Space, bekomme ich das "echo foo" zurück, der Cursor steht dahinter und ich kann die Kommandozeile bearbeiten bevor ich schließlich ihre Abarbeitung auslöse.

Aber nun zurück zum Shellprompt. Den oben genannten Prompt bekommt man in der Bash mit

PS1="[\#/\!]\u@\h:\w \$ "

Ich denke, das ist simpel genug dass es sich nach Lektüre von "PROMPTING" in der bash-Manpage selbst erschließt.

Viele Leute haben den Exitcode des vorher ausgeführten Kommandos im Shellprompt. Das kann man machen, mich hat die Null aber immer gestört. Hier hilft der wirklich geniale Code, den ich mir bei einem Kollegen geschnappt habe:

[3/5036]mh@swivel:~ $PS1="\$(printf '%.*s%.*s' \$? \$? \$? \" \")[\#/\!]\u@\h:\w \$ " [4/5037]mh@swivel:~ $ false 1 [5/5038]mh@swivel:~ $ 1 [5/5038]mh@swivel:~ $ 1 [5/5038]mh@swivel:~ $ true [6/5039]mh@swivel:~ $

Das ist so genial, dass es eine etwas nähere Betrachtung verdient. Wie wir alle wissen, enthält die Variable $? den Exitcode des letzten Kommandos. Weiterhin müssen wir wissen, dass der printf-Formatstring %.*s zwei Parameter verschluckt, wobei der erste Parameter die Feldbreit und der zweite Parameter der auszugebende Wert ist. printf "%.*s" 0 0 gibt demnach eine Null in einem null Zeichen breiten Feld aus (also nichts), während printf "%.*s" 123 123 den String 123 in einem 123 Zeichen breiten Feld ausgibt. Da der String kürzer ist, wird das Feld entsprechend eingekürzt.

Ist der $? also gleich Null, kommt aus dem $()-Ausdruck nichts heraus. Ist $? ungleich Null, gibt das $() den Inhalt von $? gefolgt von einem Leerzeichen aus - genau das, was in dieser Situation sinnvoll und gewünscht ist. Dabei bin ich richtig froh, dass mir das nicht selbst eingefallen ist - denn so kann ich meiner Begeisterung über die Genialität dieser Konstruktion hemmungslos freien Lauf lassen, ohne dass ich Gefahr laufe, in die Selbstbeweihräucherung abzugleiten.

printf will man übrigens in Shellskripts viel häufiger verwenden, unter anderem weil seine Parameter besser standardisiertes sind als die von echo.

Dann machen wir das ganze noch bunt:

if [ -z "$TYPESCRIPT" ]; then LOC_CYAN="\[$(tput setaf 6)\]" LOC_REDBOLD="\[$(tput setaf 1)$(tput bold)\]" LOC_NORM="\[$(tput sgr0)\]" else LOC_CYAN="" LOC_REDBOLD="" LOC_NORM="" fi

if [ $UID -ne 0 ]; then LOC_PROMPTCOLOR="$LOC_CYAN" else LOC_PROMPTCOLOR="$LOC_REDBOLD" fi

PS1="\$(printf '%.*s%**s' \$? \$? \$? \" \")${LOC_PROMPTCOLOR}[\#/\!]\u@\h:\w \$ ${LOC_NORM} "

tput gibt die zum aktuellen Setting von TERM passenden Steuerzeichen für die gewünschte Ausgabeveränderung auf seiner Standardausgabe aus. Der Code macht den Shellprompt für root (wenn der Code in den Bash-Init-Files von root steht) tiefrot, den normalen Shellprompt cyan. Die script(1) erkennende Konstruktion um die setaf-Aufrufe herum ist wohl heutzutage nicht mehr notwendig, da script(1) TERM von sich aus auf dumb zu setzen scheint.

Nun noch git. Dafür gibt es /usr/lib/git-core/git-sh-prompt, das zumindest auf Debian zum git-Paket gehört. Die Dokumentation findet sich ebenfalls in git-sh-prompt.

LOC_GITPROMPT=""
if [ -x "$(command -v git)" ] && type -t __git_ps1 &>/dev/null; then
  # see /usr/lib/git-core/git-sh-prompt
  export GIT_PS1_SHOWDIRTYSTATE=1
  export GIT_PS1_SHOWSTASHSTATE=1
  export GIT_PS1_SHOWUNTRACKEDFILES=1
  export GIT_PS1_SHOWUPSTREAM="auto verbose git"
  export GIT_PS1_SHOWCOLORHINTS=1
  # shellcheck disable=SC2016
  LOC_GITPROMPT='$(__git_ps1 "(%s) ")'
fi

PS1="\\$(printf '%.*s%.*s' \\$? \\$? \\$? \\" \\")${LOC_PROMPTCOLOR}[\#/\\!]\\u@\\h:\\w ${LOC_GITPROMPT}\\$ ${LOC_NORM} "
[19/5045]mh@swivel:~ $ mkdir keks
[20/5046]mh@swivel:~ $ cd keks                                                  
/home/mh/keks                                                                   
[21/5047]mh@swivel:~/keks $ git init
Initialized empty Git repository in /home/mh/keks/.git/                         
[22/5048]mh@swivel:~/keks (master #) $ echo foo > foo
[23/5049]mh@swivel:~/keks (master #%) $ git add foo                             
[24/5050]mh@swivel:~/keks (master +) $ git commit -m 'add foo'
[master (root-commit) 710d0b0] add foo                                          
 1 file changed, 1 insertion(+)
 create mode 100644 foo
[25/5051]mh@swivel:~/keks (master) $ git checkout -b otherbranch
Switched to a new branch 'otherbranch'
[26/5052]mh@swivel:~/keks (otherbranch) $ cd ~/git/zgserver/
[27/5053]mh@swivel:~/git/zgserver (master u=) $ 

Dieser Prompt zeigt Dir an, ob Du Dich in einem git-worktree befindest, in welchem Branch Du gerade bist, ob Du ungeaddetes oder uncommittetes Zeug in Deinem Worktree stehen hast, und ob Du in dem Fall, dass es einen zugeordneten remote tracking branch gibt, diesem voraus oder hinterher bist.

Trackbacks

No Trackbacks

Comments

Display comments as Linear | Threaded

mitch on :

Obacht: Irgendein Plugin macht den Quellcode „hübsch“ und der Shellcode ist voller typographischer Anführungszeichen, das ist fies zu kopieren :-)

Marc 'Zugschlus' Haber on :

Ja, das ist leider richtig. Und ich weiß auch nciht, wie ich dem s9y das - nur für den Quellcode - abgwöhnen soll. Muss mal darüber nachdenken, ob ich die Scripts noch als plain file zum download bereitstelle.

Add Comment

Markdown format allowed
Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications.
Form options