ความแตกต่างระหว่าง PS1 และ PROMPT_COMMAND คืออะไร


108

ในขณะที่ดูหัวข้อที่ยอดเยี่ยมนี้ฉันสังเกตเห็นว่าบางตัวอย่างใช้

PS1="Blah Blah Blah"

และการใช้งานบางอย่าง

PROMPT_COMMAND="Blah Blah Blah"

(และบางคนใช้ทั้งสองอย่าง) เมื่อตั้งค่าพรอมต์ใน bash shell อะไรคือความแตกต่างระหว่างทั้งสอง? การค้นหา SO และการค้นหาด้วย Google ที่กว้างขึ้นเล็กน้อยก็ไม่ได้รับผลลัพธ์ดังนั้นแม้แต่ลิงก์ไปยังตำแหน่งที่ถูกต้องเพื่อค้นหาคำตอบก็จะได้รับการชื่นชม

คำตอบ:



67

PROMPT_COMMAND สามารถมีคำสั่ง bash ธรรมดาในขณะที่ตัวแปร PS1 ยังสามารถมีอักขระพิเศษเช่น "\ h" สำหรับชื่อโฮสต์ในตัวแปร

ตัวอย่างเช่นนี่คือพร้อมต์ bash ของฉันที่ใช้ทั้ง PROMPT_COMMAND และ PS1 รหัส bash ใน PROMPT_COMMAND ระบุว่าคุณอาจอยู่ในสาขาคอมไพล์ใดและแสดงสิ่งนั้นที่พรอมต์พร้อมกับสถานะการออกของกระบวนการรันล่าสุดชื่อโฮสต์และชื่อฐานของ pwd ตัวแปร RET เก็บค่าส่งคืนของโปรแกรมที่เรียกใช้ล่าสุด วิธีนี้สะดวกในการดูว่ามีข้อผิดพลาดหรือไม่และรหัสข้อผิดพลาดของโปรแกรมล่าสุดที่ฉันรันในเทอร์มินัล สังเกต 'ด้านนอก' รอบ ๆ นิพจน์ PROMPT_COMMAND ทั้งหมด รวมถึง PS1 เพื่อให้ตัวแปรนี้ถูกประเมินใหม่ทุกครั้งที่ประเมินตัวแปร PROMPT_COMMAND

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";'

ผลลัพธ์ตัวอย่างมีลักษณะเช่นนี้ในไดเร็กทอรี non-git:

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

และในไดเร็กทอรี git คุณจะเห็นชื่อสาขา:

sashan@dhcp-au-122 rework mybranch $ 

อัปเดต

หลังจากอ่านความคิดเห็นและคำตอบของ Bob ฉันคิดว่าการเขียนตามที่เขาอธิบายนั้นดีกว่า มันสามารถบำรุงรักษาได้มากกว่าสิ่งที่ฉันเขียนไว้ข้างต้นโดยที่ตัวแปร PS1 ถูกตั้งค่าไว้ใน PROMPT_COMMAND ซึ่งเป็นสตริงที่ซับซ้อนมากซึ่งได้รับการประเมินในรันไทม์โดย bash ใช้งานได้ แต่ซับซ้อนกว่าที่จำเป็น เพื่อความเป็นธรรมฉันเขียนว่า PROMPT_COMMAND สำหรับตัวฉันเองเมื่อประมาณ 10 ปีที่แล้วและได้ผลและไม่ได้คิดมากเกินไปเกี่ยวกับเรื่องนี้

สำหรับผู้ที่สงสัยว่าฉันแก้ไขสิ่งต่าง ๆ ของฉันอย่างไรโดยพื้นฐานแล้วฉันได้ใส่รหัสสำหรับ PROMPT_COMMAND ในไฟล์แยกต่างหาก (ตามที่ Bob อธิบายไว้) จากนั้นสะท้อนสตริงที่ฉันตั้งใจจะเป็น 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"

และใน. bashrc ของฉัน

function prompt_command {
    RET=$?
    export PS1=$(~/.bash_prompt_command $RET)
}
PROMPT_DIRTRIM=3
export PROMPT_COMMAND=prompt_command

1
คุณสามารถย่อบรรทัดใดบรรทัดหนึ่งให้สั้นลง: if git branch &>/dev/null ; then\ . มันเปลี่ยนเส้นทางทั้ง stdout และ stderr ไปที่ / dev / null tldp.org/LDP/abs/html/io-redirection.html

3
มีความจำเป็นที่จะไม่มีการส่งออก PROMPT_COMMAND
dolmen

2
ฉันคิดว่าความคิดเห็นของ ceving เป็นจริงมากสำหรับคำตอบนี้เช่นกัน:Don't set PS1 in PROMPT_COMMAND! Set variables in PROMPT_COMMAND and use them in PS1
phil294

2
ฉันไม่เห็นเหตุผลว่าทำไมการเปลี่ยนแปลงPS1ภายในออนไลน์PROMPT_COMMANDจึงเสียเปรียบ เป็นรหัสที่มีประโยชน์ที่สมบูรณ์แบบ ตรงกันข้ามกับคำตอบของ Bob PS1ตัวแปรถูกสร้างขึ้นอย่างถูกต้อง สิ่งนี้ช่วยให้ bash prompt ซับซ้อนขึ้นมากขึ้นอยู่กับสถานการณ์จริงของคุณ
Christian Wolf

2
@ChristianWolf โครงสร้างPS1ภายในPROMPT_COMMANDไม่มีจุดประสงค์ เป็นตัวอย่างว่าจะไม่ทำ สร้างPS1ครั้งเดียว.bash_profileเพียงใช้เครื่องหมายคำพูดเดี่ยวแทนเครื่องหมายคำพูดคู่ดังนั้นการแทนที่ตัวแปรจะได้รับการประเมินในแต่ละพรอมต์
เพื่อน

46

ความแตกต่างคือ PS1 เป็นสตริงพร้อมต์จริงที่ใช้และ PROMPT_COMMAND เป็นคำสั่งที่ดำเนินการก่อนพร้อมต์ หากคุณต้องการวิธีสร้างพรอมต์ที่ง่ายและยืดหยุ่นที่สุดให้ลองทำดังนี้

ใส่สิ่งนี้ใน. bashrc ของคุณ:

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

จากนั้นเขียนสคริปต์ (bash, perl, ruby: your choice) และวางไว้ใน ~ / bin / bash_prompt

สคริปต์สามารถใช้ข้อมูลใด ๆ ที่ต้องการสร้างพร้อมต์ นี่เป็น IMO ที่ง่ายกว่ามากเพราะคุณไม่จำเป็นต้องเรียนรู้ภาษาทดแทนแบบพิสดารที่พัฒนาขึ้นสำหรับตัวแปร PS1 เท่านั้น

คุณอาจคิดว่าคุณสามารถทำได้โดยเพียงแค่ตั้งค่า PROMPT_COMMAND ไปที่ ~ / bin / bash_prompt โดยตรงและตั้งค่า PS1 เป็นสตริงว่าง ในตอนแรกดูเหมือนจะใช้งานได้ แต่ในไม่ช้าคุณจะพบว่าโค้ด readline คาดว่า PS1 จะถูกตั้งค่าเป็นพรอมต์จริงและเมื่อคุณเลื่อน backwords ในประวัติสิ่งต่างๆก็จะยุ่งเหยิงตามมา วิธีแก้ปัญหานี้ทำให้ PS1 แสดงพรอมต์ล่าสุดเสมอ (เนื่องจากฟังก์ชั่นตั้งค่า PS1 จริงที่ใช้โดยอินสแตนซ์การเรียกใช้ของเชลล์) และทำให้ประวัติการอ่านและประวัติคำสั่งทำงานได้ดี


17
อย่าPS1เข้าPROMPT_COMMAND! ตั้งค่าตัวแปรในและใช้พวกเขาในPROMPT_COMMAND PS1มิฉะนั้นคุณคุณจะหลวมความสามารถในการใช้PS1ลำดับหนีเหมือนหรือ\u \hคุณต้องสร้างใหม่ในPROMPT_COMMAND. อาจเป็นไปได้ แต่ไม่สามารถหลีกเลี่ยงการสูญเสีย\[และ\]เครื่องหมายจุดเริ่มต้นและจุดสิ้นสุดของอักขระที่ไม่สามารถพิมพ์ได้ ซึ่งหมายความว่าคุณไม่สามารถใช้สีได้โดยไม่ต้องสับสนกับเทอร์มินัลเกี่ยวกับความยาวของพรอมต์ และสิ่งนี้ทำให้สับสนreadlineเมื่อแก้ไขคำสั่งที่วางไข่สองบรรทัด ในที่สุดคุณก็มีปัญหาใหญ่บนหน้าจอ
สิ้นสุด

1
@ceving จริงนั่น! อาจใช้ PROMPT_COMMAND เพื่อเปลี่ยนรูปแบบ PS1 ของคุณและรับสิ่งที่ดีที่สุดจากทั้งสองโลก
ฤษ

3
PROMPT_COMMANDPS1จะถูกดำเนินการก่อนที่จะพิมพ์ ฉันไม่เห็นปัญหาในการตั้งค่าPS1จากภายในPROMPT_COMMANDเพราะหลังจากPROMPT_COMMANDเสร็จสิ้นเชลล์จะพิมพ์PS1ซึ่งแก้ไขจากPROMPT_COMMAND(หรือในกรณีนี้คือด้านในprompt_command)?
Felipe Alvarez

3
คำเตือน: โดยทั่วไปไม่ควรใช้ PROMPT_COMMAND เพื่อพิมพ์อักขระไปยังพรอมต์โดยตรง อักขระที่พิมพ์นอก PS1 จะไม่ถูกนับโดย Bash ซึ่งจะทำให้วางเคอร์เซอร์ไม่ถูกต้องและล้างอักขระ ใช้ PROMPT_COMMAND เพื่อตั้งค่า PS1 หรือดูที่คำสั่งฝัง (ที่มาArch Wiki )
ผล

3
ฉันไม่เข้าใจว่าทำไมทุกคนถึงพยายามทำเทคนิคบางอย่างใน PROMPT_COMMAND แทนที่จะใช้แค่การแทนที่คำสั่งใน PS1 export PS1='$(~/bin/bash_prompt)'ทำให้สิ่งเดียวกันนี้มีข้อผิดพลาด
เพื่อน

10

จากman bash:

PROMPT_COMMAND

หากตั้งค่าไว้ค่าจะดำเนินการเป็นคำสั่งก่อนที่จะออกพรอมต์หลักแต่ละรายการ

PS1

ค่าของพารามิเตอร์นี้ถูกขยาย (ดู PROMPTING ด้านล่าง) และใช้เป็นสตริงพร้อมต์หลัก ค่าเริ่มต้นคือ '' \ s- \ v \ $ ''

หากคุณต้องการตั้งค่าสตริงพร้อมต์การใช้PS1เพียงอย่างเดียวก็เพียงพอแล้ว:

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

PROMPT_COMMANDหากคุณต้องการที่จะทำอย่างอื่นก่อนที่จะพิมพ์พรอมต์การใช้งาน ตัวอย่างเช่นหากคุณต้องการซิงค์การเขียนแคชลงในดิสก์คุณสามารถเขียน:

PROMPT_COMMAND='sync'

1
นอกจากนี้คุณยังสามารถตั้งค่าชื่อของสถานีจากPS1โดยไม่จำเป็นต้องPROMPT_COMMANDเป็นลำดับที่ตั้งชื่อสามารถรวมอยู่ในPS1ห่อด้วยและ\[ \]
dolmen

1
@dolmen เอาล่ะ จากนั้นลองทำอย่างอื่นเช่นการตั้งค่าตัวแปรสภาพแวดล้อมแบบไดนามิก
Cyker

@Cyker คุณสามารถตั้งค่าตัวแปรสภาพแวดล้อมแบบไดนามิกPS1ได้มันจะถูกตั้งค่าใน subshell ดังนั้นคุณจึงไม่สามารถรับค่ากลับมาได้ แต่ตัวอย่างของคุณเป็นเรื่องเล็กน้อยPS1='$(sync)user \u on host \h$ '
เพื่อน

1

ความแตกต่างก็คือ

  • หากคุณส่งออกบรรทัดที่ไม่สมบูรณ์PROMPT_COMMANDมันจะทำให้ข้อความ bash ของคุณเสียหาย
  • PS1ทดแทน\Hและเพื่อน
  • PROMPT_COMMANDเรียกใช้เนื้อหาPS1ใช้เนื้อหาเป็นพรอมต์

PS1ทำการขยายตัวแปรและการแทนที่คำสั่งในแต่ละพรอมต์โดยไม่จำเป็นต้องใช้PROMPT_COMMANDเพื่อกำหนดค่าPS1หรือรันโค้ดโดยอำเภอใจ คุณสามารถทำexport PS1='$(uuidgen) $RANDOM'ครั้งเดียวใน.bash_profileเพียงใช้ราคาเดียว


0

ใช่แล้วเพื่อพยายามที่จะทำให้สิ่งนี้ลง:

  • PROMPT_COMMANDเป็นตัวแปร / ฟังก์ชันความสะดวกในการทุบตีที่มีประโยชน์แต่มีการพูดอย่างเคร่งครัดไม่มีอะไรที่ไม่สามารถทำได้โดยใช้PS1เพียงอย่างเดียวถูกต้อง?

ฉันหมายความว่าถ้าใครต้องการตั้งค่า ตัวแปรอื่นที่มีขอบเขตนอกพรอมต์: ขึ้นอยู่กับเชลล์ตัวแปรนั้นอาจต้องได้รับการประกาศก่อนภายนอก$PS1หรือ (กรณีที่แย่ที่สุด) อาจต้องมีบางอย่างที่รออยู่ใน FIFO ก่อน การโทร$PS1(และติดอาวุธอีกครั้งในตอนท้ายของ$PS1); \u \hอาจก่อให้เกิดปัญหาบางอย่างโดยเฉพาะอย่างยิ่งถ้าคุณกำลังใช้ regex แฟนซีบาง; แต่อย่างอื่น: เราสามารถทำทุกอย่างให้สำเร็จได้PROMPT_COMMANDโดยใช้การแทนที่คำสั่งภายใน$PS1(และในบางกรณีอาจเป็นในกรณีมุมหรือ subshells ที่ชัดเจน)?

ขวา?

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.