Allinea a destra parte del prompt


27

Sono sicuro di aver visto qualcuno avere una parte del suo prompt allineata a destra nella finestra del terminale e quindi avviare il cursore effettivo su una seconda riga. So che posso ottenere la seconda riga con un "\ n" in PS1, ma non riesco a capire come allinearne una parte a destra. Quello che ho visto era solo uno spazio bianco aggiunto tra le due stringhe?

Risposte:


17

Quello che vuoi può essere fatto abbastanza facilmente visualizzando la prima riga prima di visualizzare il prompt. Ad esempio, di seguito viene visualizzato un prompt di \wa sinistra della prima riga e un prompt di \u@\ha destra della prima riga. Usa la $COLUMNSvariabile che contiene la larghezza del terminale e il $PROMPT_COMMANDparametro che viene valutato prima che bash visualizzi il prompt.

print_pre_prompt () 
{ 
    PS1L=$PWD
    if [[ $PS1L/ = "$HOME"/* ]]; then PS1L=\~${PS1L#$HOME}; fi
    PS1R=$USER@$HOSTNAME
    printf "%s%$(($COLUMNS-${#PS1L}))s" "$PS1L" "$PS1R"
}
PROMPT_COMMAND=print_pre_prompt

3
Si noti che le cose diventano significativamente più complicate se si desidera un prompt di sinistra colorato, poiché i caratteri non stampabili indicano che la lunghezza della stringa non è uguale al numero di caratteri visualizzati.
Mu Mind

1
Sia questa che la risposta più votata non funzionano correttamente se .inputrcha set show-mode-in-prompt on. Entrambi non calcolano la lunghezza dei codici ANSI CSI non stampabili e non li racchiudono correttamente \[e \]come menzionato da @Mu Mind. Vedi questa risposta per una risoluzione.
Tom Hale,

26

Sulla base delle informazioni che ho trovato qui sono stato in grado di scoprire una soluzione più semplice per l'allineamento a destra, adattando al contempo il contenuto a lunghezza variabile a destra o a sinistra, incluso il supporto per il colore. Aggiunto qui per comodità ...

Nota sui colori: l' utilizzo \033dell'evasione a favore di alternative, senza \[\]raggruppamenti, risulta il più compatibile e quindi consigliato.

Il trucco è di scrivere prima il lato destro, quindi utilizzare il ritorno \ra capo ( ) per tornare all'inizio della riga e continuare a sovrascrivere il contenuto del lato sinistro sopra, come segue:

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

Sto usando tput colssu Mac OS X per recuperare la larghezza del terminale / console dal terminfomomento che il mio $COLUMNSvar non è popolato envma puoi sostituire il " *" valore sostituibile %*s, fornendo " ${COLUMNS}", o qualsiasi altro valore che preferisci, invece.

L'esempio seguente utilizza $RANDOMper generare contenuti di diversa lunghezza, compresi i colori e mostra come è possibile estrarre funzioni per trasformare l'implementazione in funzioni riutilizzabili.

function prompt_right() {
  echo -e "\033[0;36m$(echo ${RANDOM})\033[0m"
}

function prompt_left() {
  echo -e "\033[0;35m${RANDOM}\033[0m"
}

function prompt() {
    compensate=11
    PS1=$(printf "%*s\r%s\n\$ " "$(($(tput cols)+${compensate}))" "$(prompt_right)" "$(prompt_left)")
}
PROMPT_COMMAND=prompt

Poiché printfsupponiamo che la lunghezza della stringa sia il numero di caratteri che dobbiamo compensare per la quantità di caratteri richiesti per il rendering dei colori, la troverai sempre a corto di fine schermo a causa dei caratteri ANSI non stampati senza compensazione. I caratteri richiesti per il colore rimangono costanti e scoprirai che anche printf tiene conto del cambio di lunghezza, come restituito $RANDOMper esempio ', che mantiene il nostro giusto allineamento in tatto.

Questo non è il caso con particolare bash sequenze di escape rapida (es. \u, \w, \h, \t) Se, come questi saranno registrare soltanto una lunghezza di 2 perché bash tradurrà solamente quando viene visualizzata la richiesta, dopo printf ha reso la stringa. Ciò non influisce sul lato sinistro, ma è meglio evitarli a destra.

Non ha alcuna conseguenza se il contenuto generato rimarrà comunque costante. Come con l' \topzione time che renderà sempre la stessa quantità di caratteri (8) per 24 volte. Abbiamo solo bisogno di considerare la compensazione richiesta per tener conto della differenza tra 2 caratteri contati che risulta in 8 casi quando viene stampato, in questi casi.

Tieni presente che potresti dover triplicare \\\alcune sequenze di escape che altrimenti hanno significato per le stringhe. Come nell'esempio seguente, l'attuale escape della directory di lavoro \wnon ha alcun significato, quindi funziona come previsto, ma il tempo \t, che significa un carattere di tabulazione, non funziona come previsto senza prima averlo salvato tre volte.

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)+${compensate}))" "$(prompt_right)" "$(prompt_left)")
}
PROMPT_COMMAND=prompt

nJoy!


8

Usando printfcon ha $COLUMNSfunzionato davvero bene, qualcosa del tipo:

printf "%${COLUMNS}s\n" "hello"

Lo giustificava perfettamente per me.


6

Quanto segue metterà la data e l'ora correnti in ROSSO sull'RHS del terminale.

# 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="\[${Save}\e[${COLUMNS:-$(tput cols)}C\e[${#PS1RHS_stripped}D${PS1RHS}${Rest}\]${PS1}"

vantaggi:

  • Funziona correttamente con i colori e i codici ANSI CSI nel prompt RHS
  • Nessun sottoprocesso. shellcheckpulito.
  • Funziona correttamente se .inputrcha set show-mode-in-prompt on.
  • Incapsula correttamente i caratteri che non richiedono una lunghezza di prompt \[e in \]modo che la modifica del testo immesso al prompt non causi la ristampa del prompt in modo strano.

Nota : È necessario garantire che eventuali sequenze di colori nella $PS1prima questo codice è exeucted siano racchiusi in \[e \], e che non v'è alcuna nidificazione di loro.


mentre in teoria mi piace questo approccio, in pratica non funziona immediatamente (Ubuntu 18.04, GNU bash 4.4.19): aggiungendo il codice direttamente in .bashrc viene prima visualizzato l'errore bash: local: can only be used in a function, che è banale da risolvere, e dopo ciò, non mostra nulla perché COLUMNSnon è definito: deve essere sostituito con $(tput cols). stesso risultato se lo snippet viene salvato in un file diverso e quindi inserito in .bashrc.
Polentino,

1
Grazie @Polentino. Ho aggiornato il codice per l'esecuzione tput colsse $COLUMNSnon è impostato. E sì, questo codice dovrebbe essere all'interno di una funzione. Uso PROMPT_COMMAND='_prompt_bash_set'e nome la funzione _prompt_bash_set.
Tom Hale,

2

Ho solo pensato di buttare il mio qui. È quasi esattamente lo stesso del prompt zsh di GRML (tranne gli aggiornamenti di zsh è un po 'meglio su nuove linee e back-space - che è impossibile replicare in bash ... beh molto difficile in questo momento, almeno).

Ho trascorso tre giorni buoni su questo (testato solo su un laptop che esegue arch), quindi ecco uno screenshot e poi le cose che vanno nel mio ~ / .bashrc :)

screenshot del prompt di bash in azione

attenzione - è un po 'folle

importante a parte - ogni ^[(come ^[[34m) è davvero il personaggio di fuga (char)27. L'unico modo per sapere come inserirlo è quello di inserire ctrl+ ( [v) (cioè premere entrambi [e vmentre ctrlsi tiene premuto.

# 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" "${branch}" "^[[35m" "^[[39m"
    fi;
}

# grml PS1 string
PS1="\n\[\e[F\e[0m\]\$(_PS1_ret)\[\e[34;1m\]${debian_chroot:+($debian_chroot)}\u\[\e[0m\]@\h \[\e[01m\]\w\$(_PS1_git) \[\e[0m\]% "

Sto ancora lavorando per rendere i colori configurabili, ma sono contento dei colori come sono adesso.


Attualmente sto lavorando a una correzione per il ^[personaggio pazzo e la facile commutazione del colore :)


Non è Ctrl + [e v contemporaneamente, è Ctrl + v seguito da Ctrl + [.
NieDzejkob,


0

Aggiungendo la risposta di Giles, ho scritto qualcosa per gestire meglio i colori (a condizione che siano correttamente racchiusi in \[e \]. È caso per caso e non gestisce tutti i casi, ma mi consente di impostare la mia PS1L nella stessa sintassi della PS1 e utilizza la data (non colorata) come PS1R.

function title {
    case "$TERM" in
    xterm*|rxvt*)
        echo -en "\033]2;$1\007"
        ;;
    *)
        ;;
    esac
}

print_pre_prompt() {
    PS1R=$(date)
    PS1L_exp="${PS1L//\\u/$USER}"
    PS1L_exp="${PS1L_exp//\\h/$HOSTNAME}"
    SHORT_PWD=${PWD/$HOME/~}
    PS1L_exp="${PS1L_exp//\\w/$SHORT_PWD}"
    PS1L_clean="$(sed -r 's:\\\[([^\\]|\\[^]])*\\\]::g' <<<$PS1L_exp)"
    PS1L_exp=${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"
}

Eccolo su github: dbarnett / dotfiles / right_prompt.sh . Lo uso nel mio .bashrc in questo modo:

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

Nota: ho anche aggiunto una nuova riga dopo PS1R, che non fa alcuna differenza visiva, ma sembra che il prompt non venga confuso se si scorre indietro alcuni comandi nella cronologia dei comandi.

Sono sicuro che qualcun altro può migliorare su questo, e forse generalizzare alcuni casi speciali.


0

Ecco una soluzione basata su PROMPT_COMMANDe 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"

La magia è eseguita da:

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

Che è suddiviso per:

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, tputviene evaso con \ [\] in modo che non venga conteggiato nella lunghezza visualizzata.

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.