Rata kanan bagian prompt


27

Saya yakin bahwa saya telah melihat seseorang memiliki bagian dari prompt mereka disejajarkan ke kanan di jendela terminal mereka dan kemudian mulai kursor aktual pada baris kedua. Saya tahu bahwa saya dapat mencapai baris kedua dengan "\ n" di PS1, tetapi saya tidak tahu bagaimana menyelaraskan bagiannya ke kanan. Apakah yang saya lihat hanya spasi kosong yang ditambahkan di antara kedua string?

Jawaban:


17

Apa yang Anda inginkan dapat dengan mudah dilakukan dengan menampilkan baris pertama sebelum menampilkan prompt. Sebagai contoh, berikut ini menampilkan prompt \wdi sebelah kiri baris pertama dan prompt \u@\hdi sebelah kanan baris pertama. Itu menggunakan $COLUMNSvariabel yang berisi lebar terminal dan $PROMPT_COMMANDparameter yang dievaluasi sebelum bash menampilkan 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
Perhatikan bahwa segala sesuatunya menjadi jauh lebih rumit jika Anda menginginkan prompt kiri berwarna, karena karakter yang tidak dicetak berarti bahwa panjang string tidak sama dengan jumlah karakter yang ditampilkan.
Mu Mind

1
Baik ini dan jawaban tertinggi tidak berfungsi dengan benar jika .inputrcada set show-mode-in-prompt on. Kedua tidak menghitung panjang non-prinable kode ANSI CSI , dan tidak benar menyertakan mereka dalam \[dan \]seperti yang disebutkan oleh @Mu Mind. Lihat jawaban ini untuk resolusi.
Tom Hale

26

Berdasarkan informasi yang saya temukan di sini, saya dapat menemukan solusi yang lebih sederhana untuk menyelaraskan ke kanan sambil mengakomodasi konten panjang variabel di kanan atau kiri termasuk dukungan untuk warna. Ditambahkan di sini untuk kenyamanan Anda ...

Catatan tentang warna: menggunakan \033pelarian yang mendukung alternatif, tanpa \[\]pengelompokan, terbukti paling kompatibel dan karenanya direkomendasikan.

Caranya adalah dengan menulis sisi kanan terlebih dahulu, kemudian gunakan carriage return ( \r) untuk kembali ke garis awal dan terus menimpa konten sisi kiri di atas itu, sebagai berikut:

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

Saya menggunakan tput colsdi Mac OS X untuk mengambil lebar terminal / konsol dari terminfosejak $COLUMNSvar saya tidak terisi envtetapi Anda dapat mengganti " *" nilai yang bisa diganti %*sdengan, dengan memberikan " ${COLUMNS}", atau nilai lain yang Anda inginkan.

Contoh berikut digunakan $RANDOMuntuk menghasilkan konten dengan panjang yang berbeda termasuk warna dan menunjukkan bagaimana Anda dapat mengekstrak fungsi untuk menolak implementasi untuk fungsi yang dapat digunakan kembali.

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

Karena printfmenganggap panjang string sebagai # karakter yang kita butuhkan untuk mengkompensasi jumlah karakter yang diperlukan untuk merender warna, Anda akan menemukan selalu pendek dari ujung layar karena karakter ANSI yang tidak dicetak tanpa kompensasi. Karakter yang diperlukan untuk warna tetap konstan dan Anda akan menemukan bahwa printf juga memperhitungkan perubahan panjangnya, seperti yang dikembalikan oleh $RANDOMmisalnya ', yang menjaga perataan kanan kita tetap dalam kebijakan.

Hal ini tidak terjadi dengan urutan yang cepat escape pesta khusus (yaitu. \u, \w, \h, \t) Meskipun, karena ini hanya akan mencatat panjang 2 karena pesta hanya akan menerjemahkan mereka ketika prompt ditampilkan, setelah printf telah memberikan string. Ini tidak memengaruhi sisi kiri tetapi lebih baik menghindarinya di sisi kanan.

Tidak ada konsekuensi jika konten yang dihasilkan akan tetap pada panjang konstan. Seperti dengan \topsi waktu yang akan selalu memberikan jumlah karakter yang sama (8) selama 24 kali. Kita hanya perlu memperhitungkan kompensasi yang diperlukan untuk mengakomodasi perbedaan antara 2 karakter yang dihitung yang menghasilkan 8 karakter saat dicetak, dalam kasus ini.

Ingatlah bahwa Anda mungkin perlu meloloskan diri dari \\\beberapa urutan pelarian yang dinyatakan memiliki arti pada string. Seperti contoh berikut ini, direktori aktif saat \wini tidak memiliki arti lain sehingga berfungsi seperti yang diharapkan tetapi waktu \t, yang berarti karakter tab, tidak bekerja seperti yang diharapkan tanpa harus keluar terlebih dahulu.

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

Menggunakan printfdengan $COLUMNSbekerja dengan sangat baik, sesuatu seperti:

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

Itu benar dibenarkan untuk saya.


6

Berikut ini akan menempatkan tanggal dan waktu saat ini di RED pada RHS terminal.

# 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}"

Keuntungan:

  • Bekerja dengan benar dengan warna dan kode ANSI CSI di prompt RHS
  • Tidak ada subproses. shellcheckbersih.
  • Bekerja dengan benar jika .inputrcsudah set show-mode-in-prompt on.
  • Benar merangkum karakter non-prompt-length-memberikan \[dan \]sehingga mengedit teks yang dimasukkan pada prompt tidak menyebabkan prompt untuk mencetak ulang dengan aneh.

Catatan : Anda harus memastikan bahwa urutan warna apa pun dalam $PS1sebelum kode ini dikeluarkan dilampirkan dengan benar \[dan \]dan tidak ada penumpukannya .


sementara saya suka pendekatan ini dalam teori, dalam praktiknya tidak bekerja di luar kotak (ubuntu 18,04, GNU bash 4.4.19): menambahkan kode langsung ke. Bashrc pertama memberikan kesalahan bash: local: can only be used in a function, yang sepele untuk diperbaiki, dan setelah itu, ia tidak menunjukkan apa-apa karena COLUMNStidak didefinisikan: ia harus diganti dengan $(tput cols). hasil yang sama jika potongan disimpan pada file yang berbeda, dan kemudian bersumber ke .bashrc.
Polentino

1
Terima kasih @Polentino. Saya telah memperbarui kode untuk dijalankan tput colsjika $COLUMNStidak disetel. Dan ya, kode ini harus berada di dalam suatu fungsi. Saya menggunakan PROMPT_COMMAND='_prompt_bash_set'dan memberi nama fungsinya _prompt_bash_set.
Tom Hale

2

Saya hanya berpikir saya akan melemparkan milik saya di sini. Ini hampir persis sama dengan GRML zsh prompt (kecuali pembaruan zsh itu meminta sedikit lebih baik pada baris baru dan ruang belakang - yang tidak mungkin untuk direplikasi dalam bash ... well sangat sulit pada titik waktu ini, setidaknya).

Saya menghabiskan tiga hari yang baik untuk ini (hanya diuji pada laptop yang menjalankan lengkungan), jadi inilah screenshot dan kemudian hal-hal yang terjadi di ~ / .bashrc saya :)

tangkapan layar bash prompt dalam aksi

peringatan - ini sedikit gila

selain penting - setiap ^[(seperti ^[[34m) benar-benar karakter pelarian (char)27. Satu-satunya cara saya tahu cara memasukkan ini adalah dengan memasukkan ctrl+ ( [v) (yaitu tekan keduanya [dan vsaat ctrlditekan.

# 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\]% "

Saya masih berusaha membuat warna-warna tersebut dapat dikonfigurasi, tetapi saya senang dengan warnanya seperti sekarang.


Saat ini sedang mengerjakan perbaikan untuk ^[karakter gila dan pergantian warna yang mudah :)


Itu bukan Ctrl + [dan v secara bersamaan, itu Ctrl + v diikuti oleh Ctrl + [.
NieDzejkob


0

Menambahkan pada jawaban Giles', saya menulis sesuatu untuk menangani warna yang lebih baik (asalkan mereka benar tertutup dalam \[dan \]. Ini yang kasus per kasus dan tidak menangani setiap kasus, tetapi membuat saya dapat mengatur PS1L saya di sintaks yang sama seperti PS1 dan menggunakan tanggal (tidak berwarna) sebagai 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"
}

Ini dia di github: dbarnett / dotfiles / right_prompt.sh . Saya menggunakannya di .bashrc saya seperti ini:

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

Catatan: Saya juga menambahkan baris baru setelah PS1R, yang tidak membuat perbedaan visual, tetapi sepertinya menjaga agar prompt tidak rusak jika Anda menggulir kembali melalui perintah tertentu dalam riwayat perintah Anda.

Saya yakin orang lain dapat memperbaiki ini, dan mungkin menggeneralisasi beberapa kasus khusus.


0

Berikut adalah solusi berdasarkan PROMPT_COMMANDdan 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"

Keajaiban dilakukan oleh:

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

Yang dipecah oleh:

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

Di PS1, tputdiloloskan dengan \ [\] sehingga tidak dihitung dalam panjang yang ditampilkan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.