Richten Sie einen Teil der Eingabeaufforderung aus

17970
Felix Andersen

Ich bin sicher, ich habe gesehen, wie jemand einen Teil seiner Eingabeaufforderung rechts im Terminalfenster ausgerichtet hat und dann den eigentlichen Cursor in einer zweiten Zeile beginnt. Ich weiß, dass ich die zweite Zeile mit einem "\ n" in der PS1 erreichen kann, aber ich kann nicht herausfinden, wie ein Teil davon nach rechts ausgerichtet wird. War das, was ich sah, nur ein Leerzeichen zwischen den beiden Saiten?

22

8 Antworten auf die Frage

25
nickl-

Basierend auf den Informationen, die ich hier fand, konnte ich eine einfachere Lösung zum Rechtsausrichten finden, während Inhalte mit variabler Länge auf der rechten oder linken Seite angezeigt werden, einschließlich der Unterstützung von Farbe. Hier hinzugefügt für Ihre Bequemlichkeit ...

Hinweis zu Farben: Die Verwendung der \033Flucht zugunsten von Alternativen ohne \[\]Gruppierungen erweist sich als am besten geeignet und wird daher empfohlen.

Der Trick besteht darin, zuerst die rechte Seite zu schreiben, dann mit dem Wagenrücklauf ( \r) zum Zeilenanfang zurückzukehren und den Inhalt der linken Seite wie folgt zu überschreiben:

prompt() { PS1=$(printf "%*s\r%s\n\$ " "$(tput cols)" 'right' 'left') } PROMPT_COMMAND=prompt 

Ich verwende tput colsunter Mac OS X, um die Breite des Terminals / der Konsole abzurufen, terminfoda mein $COLUMNSvar nicht eingetragen ist. envSie können stattdessen den ersetzbaren *Wert " " ersetzen %*s, indem $Sie stattdessen " " oder einen anderen Wert angeben.

Das nächste Beispiel $RANDOMzum Generieren von Inhalten unterschiedlicher Länge enthält Farben und zeigt, wie Sie Funktionen extrahieren können, um die Implementierung in wiederverwendbare Funktionen umzuwandeln.

function prompt_right() { echo -e "\033[0;36m$(echo $)\033[0m" }  function prompt_left() { echo -e "\033[0;35m$\033[0m" }  function prompt() { compensate=11 PS1=$(printf "%*s\r%s\n\$ " "$(($(tput cols)+$))" "$(prompt_right)" "$(prompt_left)") } PROMPT_COMMAND=prompt 

Da printfangenommen wird, dass die Länge der Zeichenfolge die Anzahl der Zeichen ist, die zum Kompensieren der zum Rendern der Farben erforderlichen Anzahl von Zeichen erforderlich sind, werden Sie feststellen, dass das Ende des Bildschirms aufgrund der nicht gedruckten ANSI-Zeichen ohne Kompensation immer knapp ist. Die für die Farbe erforderlichen Zeichen bleiben konstant, und Sie werden feststellen, dass printf auch die Längenänderung berücksichtigt, wie sie $RANDOMbeispielsweise von ' zurückgegeben wird ', wodurch unsere richtige Ausrichtung erhalten bleibt.

Dies ist nicht der Fall mit speziellen Bash - Prompt - Escape - Sequenzen (dh. \u, \w, \h, \t), Obwohl, da diese nur eine Länge von 2 aufzeichnen, weil bash sie nur übersetzen, wenn die Eingabeaufforderung angezeigt wird, nachdem printf die Zeichenfolge verdient gemacht hat. Dies wirkt sich nicht auf die linke Seite aus, am besten jedoch, um sie rechts zu vermeiden.

Ohne Folgen, wenn der erzeugte Inhalt jedoch konstant bleibt. Wie bei der \tZeitoption, die immer 24 Mal die gleiche Anzahl von Zeichen (8) wiedergibt. Wir müssen nur die erforderliche Kompensation berücksichtigen, um den Unterschied zwischen zwei gezählten Zeichen auszugleichen, was in diesen Fällen beim Drucken zu 8 Zeichen führt.

Denken Sie daran, dass Sie möglicherweise \\\einige Escape-Sequenzen, die ansonsten eine Bedeutung für Strings haben, möglicherweise dreifach umgehen müssen. Wie im folgenden Beispiel hat das aktuelle Arbeitsverzeichnis-Escape \wkeine Bedeutung. Andernfalls funktioniert es wie erwartet, aber die Zeit \t( dh ein Tabulatorzeichen) funktioniert nicht wie erwartet, ohne dass zuvor ein dreifaches Escape- Verfahren durchgeführt wurde .

function prompt_right() { echo -e "\033[0;36m\\\t\033[0m" }  function prompt_left() { echo -e "\033[0;35m\w\033[0m" }  function prompt() { compensate=5 PS1=$(printf "%*s\r%s\n\$ " "$(($(tput cols)+$))" "$(prompt_right)" "$(prompt_left)") } PROMPT_COMMAND=prompt 

nJoy!

16
Gilles

Was Sie möchten, können Sie ganz einfach tun, indem Sie die erste Zeile vor der Aufforderung anzeigen. Im Folgenden wird beispielsweise eine Aufforderung \wlinks von der ersten Zeile und eine Aufforderung \u@\hrechts von der ersten Zeile angezeigt . Es verwendet die $COLUMNSVariable, die die Breite des Terminals enthält, und den $PROMPT_COMMANDParameter, der ausgewertet wird, bevor die Eingabeaufforderung von bash angezeigt wird.

print_pre_prompt ()  {  PS1L=$PWD if [[ $PS1L/ = "$HOME"/* ]]; then PS1L=\~$; fi PS1R=$USER@$HOSTNAME printf "%s%$(($COLUMNS-${#PS1L}))s" "$PS1L" "$PS1R" } PROMPT_COMMAND=print_pre_prompt 
Beachten Sie, dass die Dinge wesentlich komplizierter werden, wenn Sie eine farbige linke Eingabeaufforderung wünschen, da die Zeichen, die nicht gedruckt werden, bedeuten, dass die Zeichenfolgenlänge nicht der Anzahl der angezeigten Zeichen entspricht. Mu Mind vor 12 Jahren 2
Sowohl diese als auch die am besten bewertete Antwort funktionieren nicht richtig, wenn für ".inputrc" ["show-mode-in-prompt" gesetzt wurde] (https://cnswww.cns.cwru.edu/php/chet/readline/ readline.html # SEC10). Beide berechnen nicht die Länge der nicht prinierbaren [ANSI-CSI-Codes] (https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes) und schließen sie nicht ordnungsgemäß in `\ [` und `\ ein. ] `wie von @Mu Mind erwähnt. Siehe [diese Antwort] (https://superuser.com/a/1203400/365890) für eine Lösung. Tom Hale vor 7 Jahren 1
7
jmervine

Die Verwendung printfmit $COLUMNShat wirklich gut funktioniert, etwa wie:

printf "%$s\n" "hello" 

Es recht rechtfertigte es perfekt für mich.

5
Tom Hale

Im Folgenden werden das aktuelle Datum und die aktuelle Uhrzeit in ROT auf der rechten Seite des Terminals angezeigt.

# Create a string like: "[ Apr 25 16:06 ]" with time in RED. printf -v PS1RHS "\e[0m[ \e[0;1;31m%(%b %d %H:%M)T \e[0m]" -1 # -1 is current time  # Strip ANSI commands before counting length # From: https://www.commandlinefu.com/commands/view/12043/remove-color-special-escape-ansi-codes-from-text-with-sed PS1RHS_stripped=$(sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <<<"$PS1RHS")  # Reference: https://en.wikipedia.org/wiki/ANSI_escape_code local Save='\e[s' # Save cursor position local Rest='\e[u' # Restore cursor to save point  # Save cursor position, jump to right hand edge, then go left N columns where # N is the length of the printable RHS string. Print the RHS string, then # return to the saved position and print the LHS prompt.  # Note: "\[" and "\]" are used so that bash can calculate the number of # printed characters so that the prompt doesn't do strange things when # editing the entered text.  PS1="\[$\e[$C\e[${#PS1RHS_stripped}D$$\]$" 

Vorteile:

  • Funktioniert korrekt mit Farben und ANSI-CSI-Codes in der RHS-Eingabeaufforderung
  • Keine Unterprozesse shellchecksauber.
  • Funktioniert einwandfrei, wenn .inputrchat set show-mode-in-prompt on.
  • Korrekt kapselt die nicht-Prompt Länge gebende Zeichen \[und \]damit die Bearbeitung von Text an der Eingabeaufforderung eingegeben nicht die Aufforderung verursacht seltsam erneut zu drucken.

Hinweis : Sie werden sicherstellen müssen, dass alle Farbverläufe in dem $PS1vor diesem Code exeucted wird, sind in ordnungsgemäß eingeschlossen \[und \]und dass es keine Verschachtelung von ihnen.

Ich mag diesen Ansatz zwar theoretisch, in der Praxis funktioniert er jedoch nicht "out of the box" (ubuntu 18.04, GNU bash 4.4.19): Wenn der Code direkt in .bashrc angehängt wird, wird zuerst der Fehler `bash: local: kann nur angezeigt Wird in einer Funktion verwendet, deren Behebung trivial ist. Danach wird nichts angezeigt, da `COLUMNS` nicht definiert ist: Es muss durch` $ (tput cols) `ersetzt werden. Dasselbe Ergebnis, wenn das Snippet in einer anderen Datei gespeichert und dann in `.bashrc` gespeichert wird. Polentino vor 6 Jahren 0
Danke @Polentino. Ich habe den Code aktualisiert, um `tput cols` auszuführen, wenn` $ COLUMNS` nicht gesetzt ist. Und ja, dieser Code sollte sich innerhalb einer Funktion befinden. Ich verwende `PROMPT_COMMAND = '_ prompt_bash_set' 'und benenne die Funktion` _prompt_bash_set`. Tom Hale vor 6 Jahren 1
2
dylnmc

Ich dachte nur, ich würde meine hier reinwerfen. Es ist fast genau das gleiche wie die GRML-zsh-Eingabeaufforderung (abgesehen von zsh-Aktualisierungen ist die Eingabeaufforderung für neue Zeilen und hintere Leerzeichen etwas besser - was in bash nicht repliziert werden kann ... zumindest zu diesem Zeitpunkt sehr schwierig).

Ich habe gute drei Tage damit verbracht (nur getestet auf einem Laptop, auf dem der Bogen läuft), also hier ein Screenshot und dann das Zeug, das in meine ~ / .bashrc geht :)

screenshot of bash prompt in action

Warnung - es ist ein bisschen verrückt

wichtig beiseite - jeder ^[(wie ^[[34m) ist wirklich der Fluchtcharakter (char)27. Die einzige Möglichkeit, wie ich das einfügen kann, ist ctrl+ ( [v) einzugeben (d. H. Beide drücken [und vsolange ctrlgedrückt halten).

# grml battery? GRML_DISPLAY_BATTERY=1  # battery dir if [ -d /sys/class/power_supply/BAT0 ]; then _PS1_bat_dir='BAT0'; else _PS1_bat_dir='BAT1'; fi  # ps1 return and battery _PS1_ret(){ # should be at beg of line (otherwise more complex stuff needed) RET=$?;  # battery if [[ "$GRML_DISPLAY_BATTERY" == "1" ]]; then if [ -d /sys/class/power_supply/$_PS1_bat_dir ]; then # linux STATUS="$( cat /sys/class/power_supply/$_PS1_bat_dir/status )"; if [ "$STATUS" = "Discharging" ]; then bat=$( printf ' v%d%%' "$( cat /sys/class/power_supply/$_PS1_bat_dir/capacity )" ); elif [ "$STATUS" = "Charging" ]; then bat=$( printf ' ^%d%%' "$( cat /sys/class/power_supply/$_PS1_bat_dir/capacity )" ); elif [ "$STATUS" = "Full" ] || [ "$STATUS" = "Unknown" ] && [ "$(cat /sys/class/power_supply/$_PS1_bat_dir/capacity)" -gt "98" ]; then bat=$( printf ' =%d%%' "$( cat /sys/class/power_supply/$_PS1_bat_dir/capacity )" ); else bat=$( printf ' ?%d%%' "$( cat /sys/class/power_supply/$_PS1_bat_dir/capacity )" ); fi; fi fi  if [[ "$RET" -ne "0" ]]; then printf '\001%*s%s\r%s\002%s ' "$(tput cols)" ":( $bat " "^[[0;31;1m" "$RET" else printf '\001%*s%s\r\002' "$(tput cols)" "$bat " fi; }  _HAS_GIT=$( type 'git' &> /dev/null );  # ps1 git branch _PS1_git(){ if ! $_HAS_GIT; then return 1; fi; if [ ! "$( git rev-parse --is-inside-git-dir 2> /dev/null )" ]; then return 2; fi branch="$( git symbolic-ref --short -q HEAD 2> /dev/null )"  if [ "$branch" ]; then printf ' \001%s\002(\001%s\002git\001%s\002)\001%s\002-\001%s\002[\001%s\002%s\001%s\002]\001%s\002' "^[[0;35m" "^[[39m" "^[[35m" "^[[39m" "^[[35m" "^[[32m" "$" "^[[35m" "^[[39m" fi; }  # grml PS1 string PS1="\n\[\e[F\e[0m\]\$(_PS1_ret)\[\e[34;1m\]$\u\[\e[0m\]@\h \[\e[01m\]\w\$(_PS1_git) \[\e[0m\]% " 

Ich arbeite immer noch daran, die Farben konfigurierbar zu machen, aber ich bin mit den Farben so, wie sie jetzt sind.


Derzeit wird an einem Fix für den verrückten ^[Charakter gearbeitet und die Farben einfach umgestellt :)

Es ist nicht gleichzeitig Strg + [und v, sondern Strg + v, gefolgt von Strg + [. NieDzejkob vor 6 Jahren 0
0
Dennis Williamson

Sie können printfdie richtige Ausrichtung verwenden:

$ printf "%10s\n" "hello" hello  $ PS1='$(printf "%10s" "$somevar")\w\$ ' 
0
Mu Mind

Zusätzlich zu Giles 'Antwort habe ich etwas geschrieben, um mit Farben besser umgehen zu können (vorausgesetzt, sie sind ordnungsgemäß in \[und eingeschlossen \]. Es ist von Fall zu Fall und kann nicht mit jedem Fall umgehen, aber ich kann meine PS1L in derselben Syntax wie PS1 einstellen.) und verwendet das (ungefärbte) Datum als PS1R.

function title { case "$TERM" in xterm*|rxvt*) echo -en "\033]2;$1\007" ;; *) ;; esac }  print_pre_prompt() { PS1R=$(date) PS1L_exp="$" PS1L_exp="$" SHORT_PWD=$ PS1L_exp="$" PS1L_clean="$(sed -r 's:\\\[([^\\]|\\[^]])*\\\]::g' <<<$PS1L_exp)" PS1L_exp=$ PS1L_exp=$ PS1L_exp=$(eval echo '"'$PS1L_exp'"') PS1L_clean=$(eval echo -e $PS1L_clean) title $PS1L_clean printf "%b%$(($COLUMNS-${#PS1L_clean}))b\n" "$PS1L_exp" "$PS1R" } 

Hier ist es auf github: dbarnett / dotfiles / right_prompt.sh . Ich verwende es in meiner .bashrc so:

source $HOME/dotfiles/right_prompt.sh PS1L='$\[\033[01;32m\]\u@\h\[\033[00m\]' PS1='\[\033[01;34m\]\w\[\033[00m\]\$ ' PROMPT_COMMAND=print_pre_prompt 

Hinweis: Ich habe auch einen neuen Zeilenumbruch nach PS1R hinzugefügt, was keinen visuellen Unterschied darstellt, aber die Aufforderung nicht durcheinanderbringt, wenn Sie bestimmte Befehle in Ihrem Befehlsverlauf zurückblättern.

Ich bin sicher, dass jemand anderes dies verbessern kann und vielleicht einige der Spezialfälle verallgemeinern kann.

0
Daniel Da Cunha

Hier ist eine Lösung basierend auf PROMPT_COMMANDund tput:

function __prompt_command() { local EXIT="$?" # This needs to be first history -a local COL=$(expr `tput cols` - 8) PS1=" \[$(tput setaf 196)\][\[$(tput setaf 21)\]\W\[$(tput setaf 196)\]]\[$(tput setaf 190)\]" local DATE=$(date "+%H:%M:%S") if [ $EXIT != 0 ]; then PS1+="\[$(tput setaf 196)\]\$" # Add red if exit code non 0 tput sc;tput cuu1; tput cuf $COL;echo "$(tput setaf 196)$DATE"; tput rc else PS1+="\[$(tput setaf 118)\]\$" tput sc;tput cuu1; tput cuf $COL;echo "$(tput setaf 118)$DATE"; tput rc fi PS1+="\[$(tput setaf 255)\] " } PROMPT_COMMAND="__prompt_command" 

Die Magie wird ausgeführt von:

tput sc;tput cuu1; tput cuf $COL;echo "$(tput setaf 196)$DATE"; tput rc 

Welches ist gegliedert nach:

tput sc # saved the cursor position tput cuu1 # up one line tput cuf $COL # move $COL characters left echo "$(tput setaf 196)$DATE" # set the colour and print the date tput rc # restore the cursor position 

In PS1 tputwird mit \ [\] ein Escapezeichen erstellt, damit es nicht in der angezeigten Länge gezählt wird.