Apa perbedaan antara PS1 dan PROMPT_COMMAND

108

Saat melihat utas yang luar biasa ini, saya perhatikan bahwa beberapa contoh digunakan

PS1="Blah Blah Blah"

dan beberapa kegunaan

PROMPT_COMMAND="Blah Blah Blah"

(dan beberapa menggunakan keduanya) saat mengatur prompt di shell bash. Apa perbedaan diantara keduanya? Pencarian SO dan bahkan sedikit pencarian google yang lebih luas tidak memberikan saya hasil, jadi bahkan tautan ke tempat yang tepat untuk mencari jawabannya akan dihargai.

Jed Daniels
sumber

Jawaban:

59

Dari halaman dokumen GNU Bash: http://www.gnu.org/software/bash/manual/bashref.html

PROMPT_COMMAND
    If set, the value is interpreted as a command to execute before
    the printing of each primary prompt ($PS1).

Saya tidak pernah menggunakannya, tetapi saya bisa menggunakan ini kembali ketika saya hanya punya sh.

Scott Thomson
sumber
67

PROMPT_COMMAND dapat berisi pernyataan bash biasa sedangkan variabel PS1 juga dapat berisi karakter khusus, seperti '\ h' untuk nama host, dalam variabel.

Misalnya di sini adalah prompt bash saya yang menggunakan PROMPT_COMMAND dan PS1. Kode bash di PROMPT_COMMAND menentukan cabang git apa yang mungkin Anda masuki dan menampilkannya pada prompt, bersama dengan status keluar dari proses yang terakhir dijalankan, nama host dan nama dasar pwd. Variabel RET menyimpan nilai kembali dari program yang terakhir dieksekusi. Ini memudahkan untuk melihat apakah ada kesalahan dan kode kesalahan dari program terakhir yang saya jalankan di terminal. Perhatikan bagian luar 'yang mengelilingi seluruh ekspresi PROMPT_COMMAND. Ini menyertakan PS1 sehingga variabel ini dievaluasi ulang setiap kali variabel PROMPT_COMMAND dievaluasi.

PROMPT_COMMAND='RET=$?;\
  BRANCH="";\
  ERRMSG="";\
  if [[ $RET != 0 ]]; then\
    ERRMSG=" $RET";\
  fi;\
  if git branch &>/dev/null; then\
    BRANCH=$(git branch 2>/dev/null | grep \* |  cut -d " " -f 2);\
  fi;
PS1="$GREEN\u@\h $BLUE\W $CYAN$BRANCH$RED$ERRMSG \$ $LIGHT_GRAY";'

Contoh keluarannya terlihat seperti ini di direktori non-git:

sashan@dhcp-au-122 Documents  $ false
sashan@dhcp-au-122 Documents  1 $ 

dan di direktori git Anda melihat nama cabang:

sashan@dhcp-au-122 rework mybranch $ 

Memperbarui

Setelah membaca komentar dan jawaban Bob saya rasa menulis seperti yang dia gambarkan lebih baik. Ini lebih bisa dipelihara daripada yang saya tulis di atas, di mana variabel PS1 diatur di dalam PROMPT_COMMAND, yang dengan sendirinya merupakan string super rumit yang dievaluasi saat runtime oleh bash. Itu berhasil, tetapi itu lebih rumit dari yang seharusnya. Agar adil saya menulis bahwa PROMPT_COMMAND untuk diri saya sendiri sekitar 10 tahun yang lalu dan itu berhasil dan tidak terlalu memikirkannya.

Bagi mereka yang penasaran tentang bagaimana saya mengubah barang-barang saya, pada dasarnya saya meletakkan kode untuk PROMPT_COMMAND di file terpisah (seperti yang dijelaskan Bob) dan kemudian menggemakan string yang saya maksudkan menjadi PS1:

GREEN="\[\033[0;32m\]"
CYAN="\[\033[0;36m\]"
RED="\[\033[0;31m\]"
PURPLE="\[\033[0;35m\]"
BROWN="\[\033[0;33m\]"
LIGHT_GRAY="\[\033[0;37m\]"
LIGHT_BLUE="\[\033[1;34m\]"
LIGHT_GREEN="\[\033[1;32m\]"
LIGHT_CYAN="\[\033[1;36m\]"
LIGHT_RED="\[\033[1;31m\]"
LIGHT_PURPLE="\[\033[1;35m\]"
YELLOW="\[\033[1;33m\]"
WHITE="\[\033[1;37m\]"
RESTORE="\[\033[0m\]" #0m restores to the terminal's default colour

if [ -z $SCHROOT_CHROOT_NAME ]; then
    SCHROOT_CHROOT_NAME=" "
fi
BRANCH=""
ERRMSG=""
RET=$1
if [[ $RET != 0 ]]; then
    ERRMSG=" $RET"
fi
if which git &>/dev/null; then
    BRANCH=$(git branch 2>/dev/null | grep \* |  cut -d " " -f 2)
else
    BRANCH="(git not installed)"
fi
echo "${GREEN}\u@\h${SCHROOT_CHROOT_NAME}${BLUE}\w \
${CYAN}${BRANCH}${RED}${ERRMSG} \$ $RESTORE"

dan di .bashrc saya

function prompt_command {
    RET=$?
    export PS1=$(~/.bash_prompt_command $RET)
}
PROMPT_DIRTRIM=3
export PROMPT_COMMAND=prompt_command
sashang
sumber
1
Anda bisa memperpendek salah satu baris Anda: if git branch &>/dev/null ; then\ . Ini mengalihkan stdout dan stderr ke / dev / null. tldp.org/LDP/abs/html/io-redirection.html
3
Tidak perlu mengekspor PROMPT_COMMAND .
dolmen
2
Saya pikir komentar ceving sangat benar untuk jawaban ini juga:Don't set PS1 in PROMPT_COMMAND! Set variables in PROMPT_COMMAND and use them in PS1
phil294
2
Saya tidak melihat alasannya, mengapa mengubah PS1online dalam PROMPT_COMMANDtidak menguntungkan. Ini adalah kode yang sangat berguna. Berbeda dengan jawaban Bob ke bawah, PS1variabel itu dibangun dengan benar. Ini memungkinkan prompt bash yang jauh lebih canggih tergantung pada situasi Anda yang sebenarnya.
Christian Wolf
2
Konstruksi bagian PS1dalam PROMPT_COMMAND@ChristianWolf tidak memiliki tujuan. ini adalah contoh bagaimana tidak melakukannya. membangun PS1sekali .bash_profile, cukup gunakan tanda kutip tunggal daripada tanda kutip ganda, sehingga penggantian variabel akan dievaluasi selama setiap prompt.
sobat
46

Perbedaannya adalah PS1 adalah string prompt sebenarnya yang digunakan, dan PROMPT_COMMAND adalah perintah yang dijalankan tepat sebelum prompt. Jika Anda menginginkan cara yang paling sederhana dan fleksibel untuk membuat prompt, coba ini:

Taruh ini di .bashrc Anda:

function prompt_command {
  export PS1=$(~/bin/bash_prompt)
}
export PROMPT_COMMAND=prompt_command

Kemudian tulis skrip (bash, perl, ruby: pilihan Anda), dan letakkan di ~ / bin / bash_prompt.

Skrip dapat menggunakan informasi apa pun yang disukainya untuk membuat prompt. Ini adalah IMO yang jauh lebih sederhana karena Anda tidak perlu mempelajari bahasa substitusi yang agak barok yang dikembangkan hanya untuk variabel PS1.

Anda mungkin berpikir bahwa Anda dapat melakukan hal yang sama hanya dengan menyetel PROMPT_COMMAND langsung ke ~ / bin / bash_prompt, dan menyetel PS1 ke string kosong. Ini pada awalnya tampaknya berfungsi, tetapi Anda segera menemukan bahwa kode readline mengharapkan PS1 diatur ke prompt sebenarnya, dan ketika Anda menggulir kata kunci dalam sejarah, hal-hal menjadi kacau sebagai hasilnya. Solusi ini menyebabkan PS1 selalu mencerminkan prompt terbaru (karena fungsi menyetel PS1 aktual yang digunakan oleh instance pemanggilan shell), dan ini membuat riwayat baris baca dan perintah berfungsi dengan baik.

Bob
sumber
17
Apakah tidak diatur PS1di PROMPT_COMMAND! Tetapkan variabel PROMPT_COMMANDdan gunakan dalam PS1. Jika tidak, Anda akan kehilangan kemampuan untuk menggunakan PS1urutan pelarian seperti \uatau \h. Anda harus menemukannya kembali PROMPT_COMMAND. Itu mungkin mungkin tetapi tidak mungkin untuk mengatasi kehilangan \[dan \]yang menandai awal dan akhir karakter yang tidak dapat dicetak. Ini berarti Anda tidak dapat menggunakan warna tanpa membingungkan terminal tentang panjang prompt. Dan ini membingungkan readlinesaat mengedit perintah yang menghasilkan dua baris. Pada akhirnya Anda memiliki kekacauan besar di layar.
ceving
1
@ceving Benar itu! Seseorang dapat menggunakan PROMPT_COMMAND untuk mengubah format PS1 Anda dan mendapatkan yang terbaik dari kedua dunia
2grit
3
PROMPT_COMMANDdijalankan sebelum mencetak PS1. Saya tidak melihat ada masalah pengaturan PS1dari dalam PROMPT_COMMAND, karena setelah PROMPT_COMMANDselesai, shell akan mencetak PS1, yang dimodifikasi dari PROMPT_COMMAND(atau dalam hal ini, di dalam prompt_command)?
Felipe Alvarez
3
Peringatan: PROMPT_COMMAND biasanya tidak boleh digunakan untuk mencetak karakter langsung ke prompt. Karakter yang dicetak di luar PS1 tidak dihitung oleh Bash, yang akan menyebabkannya salah menempatkan kursor dan menghapus karakter. Gunakan PROMPT_COMMAND untuk menyetel PS1 atau lihat perintah penyematan. ( Arch Wiki Source )
meffect
3
saya tidak mengerti mengapa semua orang mencoba melakukan beberapa trik di PROMPT_COMMAND alih-alih hanya menggunakan substitusi perintah di PS1 export PS1='$(~/bin/bash_prompt)'melakukan hal yang sama bug terlihat waras
sobat
10

Dari man bash:

PROMPT_COMMAND

Jika disetel, nilainya dijalankan sebagai perintah sebelum mengeluarkan setiap prompt utama.

PS1

Nilai parameter ini diperluas (lihat PROMPTING di bawah) dan digunakan sebagai string perintah utama. Nilai defaultnya adalah '' \ s- \ v \ $ ''.

Jika Anda hanya ingin menyetel string prompt, menggunakan PS1saja sudah cukup:

PS1='user \u on host \h$ '

Jika Anda ingin melakukan sesuatu yang lain sebelum mencetak prompt, gunakan PROMPT_COMMAND. Misalnya, jika Anda ingin menyinkronkan penulisan cache ke disk, Anda dapat menulis:

PROMPT_COMMAND='sync'
Cyker
sumber
1
Anda juga dapat menyetel judul terminal dari PS1tanpa perlu PROMPT_COMMAND, karena urutan yang menyetel judul dapat disertakan dalam PS1dibungkus dengan \[dan \].
dolmen
1
@olol Baiklah. Kemudian mari lakukan hal lain, seperti menyetel variabel lingkungan secara dinamis.
Cyker
@Cyker Anda dapat mengatur variabel lingkungan secara dinamis PS1, itu hanya akan diatur di subkulit, jadi Anda tidak bisa mendapatkan nilainya kembali. tapi PS1='$(sync)user \u on host \h$ '
sobat
1

perbedaannya adalah itu

  • jika Anda mengeluarkan baris yang tidak lengkap dari PROMPT_COMMAND, itu akan mengacaukan prompt bash Anda
  • PS1pengganti \Hdan teman
  • PROMPT_COMMANDmenjalankan isinya, PS1menggunakan isinya sebagai prompt.

PS1melakukan ekspansi variabel dan substitusi perintah pada setiap prompt, tidak perlu menggunakan PROMPT_COMMANDuntuk menetapkan nilai PS1atau menjalankan kode arbitrer. Anda dapat dengan mudah melakukannya export PS1='$(uuidgen) $RANDOM'sekali .bash_profile, cukup gunakan tanda kutip tunggal

sahabat
sumber
0

Ya, jadi untuk mencoba benar-benar memahaminya:

  • PROMPT_COMMANDadalah variabel / fungsi kenyamanan bash yang berguna , tetapi sebenarnya tidak ada yang juga tidak dapat dilakukan dengan menggunakan PS1sendiri, bukan?

Maksud saya, jika seseorang ingin mengatur variabel lain dengan ruang lingkup di luar prompt: tergantung pada shell, variabel itu mungkin perlu dideklarasikan terlebih dahulu di luar $PS1atau (kasus terburuk) seseorang mungkin harus menyukai sesuatu yang menunggu di FIFO sebelum memanggil $PS1(dan dipersenjatai lagi di akhir $PS1); yang \u \hmungkin menyebabkan masalah, terutama jika Anda menggunakan ekspresi reguler; tetapi sebaliknya: seseorang dapat mencapai apa saja PROMPT_COMMANDdengan menggunakan substitusi perintah di dalam $PS1(dan, mungkin dalam kasus sudut, subkulit eksplisit)?

Baik?

Geoff Nixon
sumber