Bash: Tampilkan status keluar dalam prompt:


11
GREEN="\e[1;32m"
RED="\e[1;31m"
NONE="\e[m"

get_exit_status(){
   es=$?
   if [ $es -eq 0 ]
   then
       echo -e "${GREEN}${es}${NONE}"
   else
       echo -e "${RED}${es}${NONE}"
   fi
}

get_path(){
    #dummy function
    echo "PATH"
}

PROMPT_COMMAND='exitStatus=$(get_exit_status)'

Berikut ini memberi saya exitStatus yang benar tetapi variabel warna tidak diperluas:

PS1='${RED}\h $(get_path) ${exitStatus}${NONE} '

Namun, yang di bawah ini, memberi saya warna tetapi status keluar tidak memperbarui:

PS1="${RED}\h $(get_path) ${exitStatus}${NONE} "

Apa cara yang tepat untuk melakukan ini? Bagaimana saya bisa memperbaikinya sehingga exitStatus dan warna keduanya berfungsi?

Jawaban:


8

Gilles mengidentifikasi masalah utama Anda, tetapi saya ingin mencoba menjelaskannya secara berbeda.

Bash menginterpretasikan prompt khusus lolos hanya sebelum memperluas variabel apa pun dalam prompt. Ini berarti bahwa menggunakan \edalam variabel yang diperluas dari prompt tidak berfungsi, meskipun itu berfungsi langsung di PS1.

Misalnya, ini berfungsi seperti yang diharapkan, dan memberikan teks merah:

PS1='\e[1;31m this is in red '

Tapi ini tidak, itu hanya menempatkan literal \edi prompt:

RED='\e[1;31m'
PS1="$RED not in red "

Jika Anda ingin menyimpan warna lolos dalam variabel, Anda dapat menggunakan ANSI-C mengutip ( $'...') untuk menempatkan karakter escape literal dalam variabel.

Untuk melakukan ini, Anda dapat mengubah definisi Anda tentang GREEN, REDdan NONE, sehingga nilai mereka adalah urutan escape yang sebenarnya.

GREEN=$'\033[1;32m'
RED=$'\033[1;31m'
NONE=$'\033[m'

Jika Anda melakukan itu, PS1tanda kutip pertama Anda dengan single harus bekerja:

PS1='${RED}\h $(get_path) ${exitStatus}${NONE} '

Namun, maka Anda akan memiliki masalah kedua.

Coba jalankan itu, lalu tekan Up Arrow, lalu Home, dan kursor Anda tidak akan kembali ke awal baris.

Untuk memperbaikinya, ubah PS1untuk memasukkan \[dan \]sekitar urutan pelarian warna, misalnya

PS1='\[${RED}\]\h $(get_path) $?\[${NONE}\] '

Anda tidak dapat menggunakan get_exit_statusdengan benar di sini, karena outputnya berisi karakter pencetakan (kode keluar) dan non-cetak (kode warna), dan tidak ada cara untuk menandainya dengan benar di prompt. Menempatkan \[...\]akan menandainya sebagai non-pencetakan secara penuh, yang tidak benar. Anda harus mengubah fungsi sehingga hanya mencetak kode warna yang tepat, dan kemudian mengelilinginya \[...\]di prompt.


\[adalah \1, dan \[sekarang \2. Mereka yang sesuai dengan beberapa RL_PROMPT_{START,END}_IGNOREhal readline yang memintanya untuk mengabaikan byte saat menghitung panjang prompt di layar. Lihat lists.gnu.org/archive/html/bug-bash/2015-08/msg00027.html .
Arthur2e5

@ Arthur2e5 Apakah maksud Anda \]adalah \2? Dan maksud Anda itu mengapa diperlukan ${exitStatus}? Maksud saya adalah bahwa ${exitStatus}tidak mengandung karakter yang tidak dicetak, jadi bash harus dapat menentukan dengan benar berapa banyak karakter yang memindahkan prompt tanpa \[dan \]dalam \[${exitStatus}\].
Mikel

Masalahnya adalah itu memang mengandung beberapa - warna. (ANSI Escapes)
Arthur2e5

@ Arthur2e5 Ew, saya benar-benar merindukan itu. :) Mengapa Anda menaruh warna ... tidak apa-apa. :)
Mikel

1
"Bash secara efektif memanggil gema di PS1 Anda, bukan gema -e" - yah itu salah atau hilang begitu saja. Bash tidak memperluas backslash-lolos seperti \edan \033(dan \[/ \], \u, dan \h) dari prompt, itu hanya melakukannya sebelum memperluas variabel. Begitu PS1='\e[1;31m red'berhasil, red='\e[1;31m'; PS1='$red red'tidak.
ilkkachu

3

Ketika Anda menjalankan PS1='${RED}\h $(get_path) ${exitStatus}${NONE} ', PS1variabel diatur ke ${RED}\h $(get_path) ${exitStatus}${NONE}, di mana hanya \hurutan escape prompt. Setelah urutan cepat diperluas (menghasilkan ${RED}darkstar $(get_path) ${exitStatus}${NONE}), shell melakukan ekspansi biasa seperti ekspansi variabel. Anda mendapatkan prompt yang ditampilkan seperti \e[1;31mdarkstar PATH 0\e[m. Tidak ada yang memperluas \eurutan untuk karakter melarikan diri yang sebenarnya.

Ketika Anda menjalankan PS1="${RED}\h $(get_path) ${exitStatus}${NONE} ", PS1variabel diatur ke \e[1;31m\h PATH 0\e[m. Variabel RED, exitStatusdan NONEdiperluas pada saat penugasan. Kemudian prompt berisi tiga escape sequence prompt ( \e, \h, dan \elagi). Tidak ada variabel shell untuk diperluas pada tahap ini.

Untuk melihat warna, Anda perlu variabel warna untuk memuat karakter pelarian aktual. Anda dapat melakukannya dengan cara ini:

RED=$'\033[1;31m'
NONE=$'\033[m'
PS1='\[${RED}\]\h \w $?\[${NONE}\] '

$'…'memperluas urutan backslash-oktal dan beberapa urutan huruf backslash seperti \n, tetapi tidak termasuk \e. Saya membuat tiga perubahan lain pada prompt Anda:

  • Gunakan \[…\]sekitar urutan non-cetak seperti perintah pengubah warna. Kalau tidak, tampilan Anda akan berantakan karena bash tidak dapat mengetahui lebar prompt.
  • \w adalah urutan pelarian bawaan untuk mencetak direktori saat ini.
  • Anda tidak perlu sesuatu yang rumit untuk ditampilkan $?di prompt jika Anda tidak memilikinya PROMPT_COMMANDsejak awal.

Saya pikir idenya adalah untuk membuat prompt menjadi hijau pada kesuksesan dan merah pada kegagalan.
mattdm

Ya, PS1salah, tetapi saran untuk penggunaan $'...'untuk REDdan GREENharus membuatnya bekerja menggunakan dogbane ini PS1.
Mikel

1

Mencoba:

PS1='`exitStatus=$?;if [ $exitStatus -eq 0 ];then echo "\['${GREEN}'\]";else echo "\['${RED}'\]";fi;echo "\h $(get_path) ${exitStatus}${NONE}"`'

1
Terima kasih, ini berhasil, tetapi apakah ada cara untuk mencapai ini tanpa harus memasukkan pernyataan-if dalam prompt?
dogbane

1

Berikut adalah pendekatan yang saya gunakan, itu menghindari penggunaan PROMPT_COMMAND.

# This function is called from a subshell in $PS1,
# to provide a colourised visual indicator of the exit status of the last run command
__COLOURISE_EXIT_STATUS() {
    # uncomment the next line for exit code output after each command, useful for debugging and testing
    #printf -- "\nexit code: $1\n" >&2
    [[ 0 == "$1" || 130 == "$1" ]] && printf -- "$GREEN" || printf -- "$RED"
}

Maka saya $PS1adalah sebagai berikut:

PS1='# ${debian_chroot:+($debian_chroot)}'"${GREEN}\u${YELLOW}@${DARK_YELLOW}\h${WHITE}:${LIGHT_BLUE}\w${WHITE}\n"'\[$(__COLOURISE_EXIT_STATUS $?)\]# \$'"\[${WHITE}\] "

1
Meskipun tidak masalah dalam kasus khusus ini, karena satu-satunya nilai yang $?dapat dimiliki adalah bilangan bulat, Anda benar-benar harus menggunakannya printf '%b' "$GREEN". Juga, hindari menggunakan nama fungsi yang diawali dengan __atau _seperti yang digunakan oleh bash-completion.
nyuszika7h

1

Ini dia - This Works For Me (TM) di Ubuntu dan Linux lainnya (Linuxen?).

Alasan untuk menempatkan deteksi kode keluar $PS1adalah bahwa satu host memiliki $PROMPT_COMMANDset baca-saja sebelum .bashrc dibaca.


0

Karena PROMPT_COMMAND, lebih bersih untuk mendefinisikan suatu fungsi dan menggunakannya:

prompt_command() {
    # ...
}
PROMPT_COMMAND=prompt_command
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.