Bash: แสดงสถานะทางออกในพรอมต์:


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)'

ต่อไปนี้ให้ exitStatus ที่ถูกต้อง แต่ตัวแปรสีไม่ได้ถูกขยาย:

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

อย่างไรก็ตามสีด้านล่างให้สีแก่ฉัน แต่สถานะการออกไม่ได้อัปเดต:

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

วิธีที่ถูกต้องในการทำเช่นนี้คืออะไร? ฉันจะแก้ไขได้อย่างไรเพื่อให้ exitStatus และสีทั้งสองทำงานได้?

คำตอบ:


8

Gilles ระบุปัญหาหลักของคุณ แต่ฉันต้องการลองอธิบายให้ต่างออกไป

Bash ตีความพรอมต์พิเศษเฉพาะก่อนที่จะขยายตัวแปรใด ๆ ในพรอมต์ ซึ่งหมายความว่าการใช้\eในตัวแปรที่ขยายจากพรอมต์ไม่ทำงานแม้ว่าจะทำงานได้โดยตรงPS1ก็ตาม

ตัวอย่างเช่นทำงานได้ตามที่คาดหวังและให้ข้อความสีแดง:

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

แต่นี่ไม่ใช่แค่ใส่ตัวอักษรลง\eในพรอมต์:

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

หากคุณต้องการเก็บค่า$'...'Escape สีในตัวแปรคุณสามารถใช้ ANSI-C quoting ( ) เพื่อใส่อักขระตัวอักษรที่แท้จริงในตัวแปร

การทำเช่นนี้คุณสามารถเปลี่ยนนิยามของGREEN, REDและNONEดังนั้นค่าของพวกเขาเป็นลำดับหนีที่เกิดขึ้นจริง

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

หากคุณทำเช่นนั้นPS1การเสนอราคาครั้งแรกของคุณควรจะทำได้:

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

อย่างไรก็ตามคุณจะมีปัญหาที่สอง

ลองเรียกใช้แล้วกดUp Arrowจากนั้นHomeเคอร์เซอร์ของคุณจะไม่กลับไปที่จุดเริ่มต้นของบรรทัด

หากต้องการแก้ไขให้เปลี่ยนPS1รวม\[และ\]รอบ ๆ ลำดับหลีกสีเช่น

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

คุณไม่สามารถใช้งานget_exit_statusได้อย่างถูกต้องที่นี่เนื่องจากเอาต์พุตมีทั้งการพิมพ์ (รหัสออก) และตัวอักษรที่ไม่ได้พิมพ์ (รหัสสี) และไม่มีวิธีการทำเครื่องหมายอย่างถูกต้องในพรอมต์ การวาง\[...\]จะทำเครื่องหมายว่าไม่ใช่การพิมพ์แบบเต็มซึ่งไม่ถูกต้อง คุณจะต้องเปลี่ยนฟังก์ชั่นเพื่อให้มันพิมพ์เฉพาะรหัสสีที่ถูกต้องจากนั้นล้อมรอบด้วย\[...\]พรอมต์


\[เป็น\1และเป็น\[ \2ผู้ที่สอดคล้องกับRL_PROMPT_{START,END}_IGNOREสิ่งที่readline บางอย่างซึ่งขอให้ละเว้นไบต์เมื่อนับความยาวพร้อมท์บนหน้าจอ ดูlists.gnu.org/archive/html/bug-bash/2015-08/msg00027.html
Arthur2e5

@ Arthur2e5 คุณหมายถึง\]คือ\2? และคุณหมายความว่าเป็นสิ่งที่จำเป็น${exitStatus}หรือไม่ จุดของฉันคือการที่${exitStatus}ไม่ได้มีอักขระที่ไม่ใช่การพิมพ์จึงทุบตีควรจะสามารถได้อย่างถูกต้องกำหนดจำนวนตัวอักษรที่มันเคลื่อนพรอมต์โดยไม่ได้\[และใน\] \[${exitStatus}\]
Mikel

ปัญหาคือมันมีบางอย่าง - สี (ANSI Escapes)
Arthur2e5

@ Arthur2e5 Ew ฉันคิดถึงมันโดยสิ้นเชิง :) ทำไมคุณต้องใส่สี ... ไม่เป็นไร :)
มิเคล

1
"Bash เรียกเสียงสะท้อนบน PS1 ของคุณอย่างมีประสิทธิภาพไม่ใช่ echo -e" - ผิดพลาดหรือพลาดจุด ทุบตีไม่ขยายทับขวา-หนีเหมือน\eและ\033(และ\[/ \], \uและ\h) จากพรอมต์มันก็ไม่ได้ก่อนที่จะขยายตัวแปร ดังนั้นใช้PS1='\e[1;31m red'งานred='\e[1;31m'; PS1='$red red'ได้ไม่
ilkkachu

3

เมื่อคุณเรียกใช้PS1='${RED}\h $(get_path) ${exitStatus}${NONE} 'ที่PS1ตัวแปรมีการตั้งค่า${RED}\h $(get_path) ${exitStatus}${NONE}ที่เพียง\hเป็นลำดับหนีพร้อมท์ หลังจากลำดับพรอมต์ถูกขยาย (ยอม${RED}darkstar $(get_path) ${exitStatus}${NONE}) เชลล์จะทำการขยายตามปกติเช่นการขยายตัวแปร คุณได้รับพรอมต์ที่ปรากฏ\e[1;31mdarkstar PATH 0\e[mขึ้น ไม่มีทางใดที่จะขยาย\eลำดับไปยังอักขระการหลบหนีที่แท้จริง

เมื่อคุณเรียกใช้PS1="${RED}\h $(get_path) ${exitStatus}${NONE} "ที่ตัวแปรมีการตั้งค่าPS1 \e[1;31m\h PATH 0\e[mตัวแปรRED, exitStatusและNONEมีการขยายตัวในช่วงเวลาของการมอบหมาย จากนั้นพรอมต์มีสามลำดับหนีพรอมต์ ( \e, \hและ\eอีกครั้ง) ไม่มีตัวแปรเชลล์ที่จะขยายในขั้นตอนนี้

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

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

$'…'ขยายลำดับทับขวา-ฐานแปดและบางลำดับทับขวาตัวอักษรเช่นแต่ไม่รวมถึง\n \eฉันได้ทำการเปลี่ยนแปลงอื่น ๆ อีกสามรายการในพรอมต์ของคุณ:

  • ใช้\[…\]รอบลำดับที่ไม่พิมพ์เช่นคำสั่งเปลี่ยนสี มิฉะนั้นจอแสดงผลของคุณจะถูกอ่านไม่ออกเนื่องจากทุบตีไม่สามารถหาความกว้างของพรอมต์ได้
  • \w เป็นลำดับ escape ในตัวเพื่อพิมพ์ไดเร็กทอรีปัจจุบัน
  • คุณไม่ต้องการให้มีอะไรซับซ้อนแสดง$?ในพรอมต์ถ้าคุณไม่มีPROMPT_COMMANDในตอนแรก

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

ใช่PS1เป็นสิ่งที่ผิด แต่คำแนะนำการใช้งาน$'...'สำหรับREDและGREENควรจะทำให้มันทำงานโดยใช้ของ PS1dogbane
Mikel

1

ลอง:

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

1
ขอบคุณใช้งานได้ แต่มีวิธีใดที่จะทำให้สำเร็จโดยไม่ต้องฝังคำสั่ง if ในพรอมต์?
dogbane

1

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

แล้วฉัน$PS1จะเป็นดังนี้:

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

1
แม้ว่าจะไม่สำคัญในกรณีนี้เนื่องจากค่าเดียวที่$?มีคือจำนวนเต็มคุณควรใช้printf '%b' "$GREEN"แทน นอกจากนี้หลีกเลี่ยงการใช้ชื่อฟังก์ชั่นนำหน้าด้วย__หรือ_ตามที่ใช้โดยการทุบตีเสร็จสิ้น
nyuszika7h

1

ไปได้เลย - ใช้งานได้กับฉัน (TM) ใน Ubuntu และ Linux อื่น ๆ (Linux หรือไม่)

เหตุผลในการวางการตรวจจับรหัสออกใน$PS1คือโฮสต์หนึ่งมีการ$PROMPT_COMMANDตั้งค่าแบบอ่านอย่างเดียวก่อนที่จะอ่าน. bashrc


0

สำหรับPROMPT_COMMANDมันสะอาดกว่าที่จะกำหนดฟังก์ชั่นและใช้งาน:

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