Domanda Configura shell per stampare sempre prompt su una nuova riga, come zsh


Se l'output di un comando non termina con a \n, il prompt successivo appare, goffamente, subito dopo:

$ echo -n hai
hai$

Ho appena notato un collega la cui shell (zsh, per quello che vale) è configurata per stampare a % (con colori di sfondo e in primo piano invertiti per enfasi) seguiti da a \n in tali casi:

$ echo -n hai
hai%
$

Mi piacerebbe fare lo stesso. Io uso Bash. È possibile? In tal caso, cosa aggiungerei al mio ~ / .bashrc?


AGGIORNARE

Ho trascorso diverse ore a capire come funziona la soluzione di gniourf_gniourf. Condividerò le mie conclusioni qui, nel caso in cui siano utili agli altri.

Ecco lo snippet pertinente dal mio .bashrc:

set_prompt() {
  # CSI 6n reports the cursor position as ESC[n;mR, where n is the row
  # and m is the column. Issue this control sequence and silently read
  # the resulting report until reaching the "R". By setting IFS to ";"
  # in conjunction with read's -a flag, fields are placed in an array.
  local curpos
  echo -en '\033[6n'
  IFS=';' read -s -d R -a curpos
  curpos[0]="${curpos[0]:2}"  # strip leading ESC[
  (( curpos[1] > 1 )) && echo -e '\033[7m%\033[0m'

  # set PS1...
}

export PROMPT_COMMAND=set_prompt

Notare la curpos[0]="${curpos[0]:2}" la linea non è necessaria L'ho incluso così questo codice potrebbe essere usato in un contesto in cui la riga è anche rilevante.


15
2017-11-13 01:23


origine


risposte:


Un piccolo trucco usando PROMPT_COMMAND:

Il valore della variabile PROMPT_COMMAND viene esaminato appena prima che Bash stampi ciascun prompt principale. Se PROMPT_COMMAND è impostato e ha un valore non nullo, quindi il valore viene eseguito come se fosse stato digitato sulla riga di comando.

Quindi, se lo metti nel tuo .bashrc:

_my_prompt_command() {
    local curpos
    echo -en "\E[6n"
    IFS=";" read -sdR -a curpos
    ((curpos[1]!=1)) && echo -e '\E[1m\E[41m\E[33m%\E[0m'
}
PROMPT_COMMAND=_my_prompt_command

sarai abbastanza buono. Sentitevi liberi di usare altri colori fantasiosi in echo "%" parte. Puoi persino inserire il contenuto di una variabile in modo che tu possa modificarla al volo.

Il trucco: ottenere la colonna del cursore (con echo -en "\E[6n" seguito dal read comando) prima di stampare il prompt e se non è 1, stampare a % e una nuova riga.

Professionisti:

  • pura bash (nessun comando esterno),
  • niente sottotitoli,
  • lascia il tuo PS1 tutto bello e pulito: se vuoi cambiare il tuo PS1 a volte (lo faccio quando lavoro in una directory profondamente annidata - non mi piace avere prompt che scorrono su diverse miglia), funzionerà ancora.

Come commenti del triplo, puoi usare stty invece di echeggiare una sequenza di controllo codificata. Ma questo usa un comando esterno e non è più puro bash. Adatta alle tue esigenze.


Riguardo al tuo problema con i brutti codici dei caratteri che vengono stampati in modo casuale: questo potrebbe essere dovuto al fatto che ci sono ancora alcune cose nel buffer tty. Potrebbero esserci diverse correzioni:

  1. Spegni e poi su echo del terminale, usando stty.

    set_prompt() {
        local curpos
        stty -echo
        echo -en '\033[6n'
        IFS=';' read -d R -a curpos
        stty echo
        (( curpos[1] > 1 )) && echo -e '\033[7m%\033[0m'
    }
    PROMPT_COMMAND=set_prompt
    

    la differenza principale è che il echo/read combo è stato avvolto con stty -echo/stty echo che rispettivamente disabilita e abilita l'eco sul terminale (ecco perché il -s opzione a read è ora inutile). In questo caso non si ottiene correttamente la posizione del cursore e questo potrebbe portare a strani messaggi di errore, oppure a % non viene emesso affatto.

  2. Cancellare esplicitamente il buffer tty:

    set_prompt() {
        local curpos
        while read -t 0; do :; done
        echo -en '\033[6n'
        IFS=';' read -s -d R -a curpos
        (( curpos[1] > 1 )) && echo -e '\033[7m%\033[0m'
    }
    PROMPT_COMMAND=set_prompt
    
  3. Basta rinunciare se il buffer tty non può essere pulito:

    set_prompt() {
        local curpos
        if ! read -t 0; then
            echo -en '\033[6n'
            IFS=';' read -s -d R -a curpos
            (( curpos[1] > 1 )) && echo -e '\033[7m%\033[0m'
        # else
        #     here there was still stuff in the tty buffer, so I couldn't query the cursor position
        fi
    }
    PROMPT_COMMAND=set_prompt
    

Come nota a margine: invece di readin un array curpos, puoi ottenere direttamente la posizione del cursore in variabili, ad esempio curx e cury come così:

IFS='[;' read -d R _ curx cury

Se hai solo bisogno della posizione y cury:

IFS='[;' read -d R _ _ cury

12
2017-11-22 23:42



Grazie a Gilles su unix.stackexchange:

È possibile fare in modo che bash visualizzi il prompt sulla riga successiva se il comando precedente ha lasciato il cursore in un punto diverso dall'ultimo margine. Metti questo nel tuo .bashrc (variazione di Ottenere gratuitamente di una proposta di Dennis Williamson)

Dalle due risposte collegate ho distillato questa soluzione:

PS1='\[\e[7m%\e[m\]$(printf "%$((COLUMNS-1))s")\r$ '

Spiegazione:

  • \[\e[7m%\e[m\] - inversione del segno di percentuale del video
  • printf "%$((COLUMNS-1))s" - COLUMNS-1 spazi. Il COLUMNS variabile memorizza la larghezza del tuo terminale se il checkwinsize le opzioni sono impostate. Dal momento che il printf è all'interno di a $() sub-shell, invece di stampare sullo schermo verrà aggiunto il suo output PS1
  • \r un carattere di ritorno a capo

Quindi, fondamentalmente, è un % firmare, una lunga sequenza di spazi, seguita da un tasto di ritorno. Funziona, ma sinceramente non capisco perché questo abbia l'effetto desiderato. In particolare, perché sembra che aggiunga un'interruzione di linea solo quando è necessaria, altrimenti non si interrompe la linea in più? Perché gli spazi sono necessari lì?


3
2017-11-22 19:10



se fate echo $PS1 vedrai il codice attuale del tuo prompt come questo:
\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\u@\h:\w\$ 

ora anteponilo con a \n come questo:

PS1="\n\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\u@\h:\w\$"

ora il tuo prompt inizierà sempre su una nuova riga.


1
2017-11-13 03:37